Memory 无法在异步任务中释放动态内存

Memory 无法在异步任务中释放动态内存,memory,rust,async-await,rust-tokio,Memory,Rust,Async Await,Rust Tokio,我们的Rust应用程序似乎有内存泄漏,我将问题归结为下面的代码示例。我还是看不出问题出在哪里 我的期望是,在(500000+1)的第条消息中,应用程序的内存将恢复到较低的水平。相反,我认为: 在发送500000条消息之前,内存使用量为124KB 发送500000条消息后,内存使用量攀升至27MB 发送500000+1消息后,内存使用率降至15.5MB 在尝试了很多东西之后,我找不到15.5MB的隐藏位置。释放内存的唯一方法是终止应用程序。Valgrind未检测到任何内存泄漏。如果能找到一个解

我们的Rust应用程序似乎有内存泄漏,我将问题归结为下面的代码示例。我还是看不出问题出在哪里

我的期望是,在(500000+1)的第条消息中,应用程序的内存将恢复到较低的水平。相反,我认为:

  • 在发送500000条消息之前,内存使用量为124KB
  • 发送500000条消息后,内存使用量攀升至27MB
  • 发送500000+1消息后,内存使用率降至15.5MB
在尝试了很多东西之后,我找不到15.5MB的隐藏位置。释放内存的唯一方法是终止应用程序。Valgrind未检测到任何内存泄漏。如果能找到一个解决方案、解决方案或指向正确方向,我们将不胜感激

可在此处找到包含以下代码的演示项目:

注释

  • 如果我删除
    self.items.push(数据)内存使用不会增加,所以我认为这不是发送方/接收方的问题
  • 弧中包装
    项目:Vec
    没有明显的记忆差异
应在其中管理内存的任务

struct处理器{
项目:Vec,
}
嵌入式处理器{
pub fn new()->Self{
处理机{
项目:Vec::new(),
}
}
发布异步fn任务(mut self,mut receiver:receiver){
而让一些(数据)=receiver.next().等待{
self.items.push(数据);
如果self.items.len()大于500000{
{
std::mem::replace(&mut self.items,Vec::new());
}
println!(“空项数组”);
}
}
println!(“处理器任务在5秒内关闭”);
东京:时间:延迟(持续时间:从秒(5))。等待;
}
}
完整的可运行示例

use std::time::Duration;

use tokio::stream::StreamExt;
use tokio::runtime::Runtime;
use tokio::sync::mpsc::{channel, Receiver, Sender};

struct Processor {
    items: Vec<String>,
}

impl Processor {
    pub fn new() -> Self {
        Processor {
            items: Vec::new(),
        }
    }

    pub async fn task(mut self, mut receiver: Receiver<String>) {
        while let Some(data) = receiver.next().await {
            self.items.push(data);

            if self.items.len() > 500000 {
                {
                    std::mem::replace(&mut self.items, Vec::new());
                }
                println!("Emptied items array");
            }
        }

        println!("Processor task closing in 5 seconds");
        tokio::time::delay_for(Duration::from_secs(5)).await;
    }
}

pub fn main() {
    {
        let mut runtime: Runtime = tokio::runtime::Builder::new()
            .threaded_scheduler()
            .core_threads(1)
            .enable_all()
            .build()
            .expect("Failed to build runtime");

        let (mut sender, receiver) = channel(1024);
        let p = Processor::new();

        runtime.spawn(async move {
            println!("Before send, waiting 5 seconds");
            tokio::time::delay_for(Duration::from_secs(5)).await;

            for i in 0..500000 {
                sender.send("Hello".to_string()).await;
            }

            println!("Sent 500,000 items, waiting 5 seconds");
            tokio::time::delay_for(Duration::from_secs(5)).await;
            sender.send("Hello".to_string()).await;

            println!("Send message to clear items");
            tokio::time::delay_for(Duration::from_secs(3)).await;

            println!("Closing sender in 5 seconds");
            tokio::time::delay_for(Duration::from_secs(5)).await;
        });

        runtime.block_on(async move {
            {
                p.task(receiver).await;
            }
            println!("Task is done, waiting 5 seconds");
            tokio::time::delay_for(Duration::from_secs(5)).await;
        });
    }

    println!("Runtime closed, waiting 5 seconds");
    std::thread::sleep(Duration::from_secs(5));
}
使用std::time::Duration;
使用tokio::stream::StreamExt;
使用tokio::runtime::runtime;
使用tokio::sync::mpsc::{通道、接收器、发送器};
结构处理器{
项目:Vec,
}
嵌入式处理器{
pub fn new()->Self{
处理机{
项目:Vec::new(),
}
}
发布异步fn任务(mut self,mut receiver:receiver){
而让一些(数据)=receiver.next().等待{
self.items.push(数据);
如果self.items.len()大于500000{
{
std::mem::replace(&mut self.items,Vec::new());
}
println!(“空项数组”);
}
}
println!(“处理器任务在5秒内关闭”);
东京:时间:延迟(持续时间:从秒(5))。等待;
}
}
pub fn main(){
{
让mut运行时:runtime=tokio::runtime::Builder::new()
.threaded_调度程序()
.核心螺纹(1)
.enable_all()
.build()
.expect(“构建运行时失败”);
let(mut发送方,接收方)=信道(1024);
设p=Processor::new();
生成(异步移动){
println!(“发送前,等待5秒”);
东京:时间:延迟(持续时间:从秒(5))。等待;
因为我在0..500000{
sender.send(“Hello.to_string())。等待;
}
println!(“发送500000个项目,等待5秒”);
东京:时间:延迟(持续时间:从秒(5))。等待;
sender.send(“Hello.to_string())。等待;
println!(“发送消息以清除项目”);
东京:时间:延迟(持续时间:从秒(3))。等待;
println!(“在5秒内关闭发送方”);
东京:时间:延迟(持续时间:从秒(5))。等待;
});
runtime.block_on(异步移动{
{
p、 任务(接收者)。等待;
}
println!(“任务完成,等待5秒”);
东京:时间:延迟(持续时间:从秒(5))。等待;
});
}
println!(“运行时关闭,等待5秒”);
std::thread::sleep(持续时间::from_secs(5));
}
Cargo.toml

[package]
name = "mem-help"
version = "0.1.0"
authors = ["Patrick Lorio <dev@plorio.com>"]
edition = "2018"

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]

futures = "0.3.1"
tokio = { version = "0.2.6", features = ["full"] }
[软件包]
name=“mem帮助”
version=“0.1.0”
作者=[“Patrick Lorio”]
edition=“2018”
#请参阅上的更多关键点及其定义https://doc.rust-lang.org/cargo/reference/manifest.html
[依赖关系]
futures=“0.3.1”
东京={version=“0.2.6”,功能=[“full”]}

泄漏是一种随着时间的推移而增长的现象,您似乎在描述这种情况,不是吗?此外,您不使用任何可能导致程序泄漏内存的东西。所以1。你为什么认为这不正常?2.如果泄漏是真的,那肯定不是你的错,所以这可能是我更大的应用程序中的一个错误,内存增长到溢出的程度(16千兆)。我的期望是,在执行
std::mem::replace(&mut self.items,Vec::new())之后内存应返回到与推送向量之前类似的级别。我可能会错过使用异步等待。如果有库错误,任何缩小它的提示都将不胜感激。值得测试,因为我说过这段代码不应该泄漏任何内存,async是非常新的,所以不要犹豫,把它作为错误发布在tokio存储库上,无论如何,我建议您阅读。这是关注点C,但锈与它有许多共同的行为。这应该可以更清楚地解释空闲内存是如何工作的。在Linux上,我建议您通过
valgrind--tool=massif
查看您的应用程序:这是一个内存分析器,它将定期输出内存使用情况的快照,对于每个快照,有多少使用情况是由于特定的堆栈跟踪造成的。这将立即允许您停止应用程序的哪个部分正在分配16G(未释放)。您可以尝试设置
MALLOC\u ARENA\u MAX=2
然后运行代码吗?如果问题在那之后消失了,那么这是一个重复。泄漏是一个随着时间的推移而增长的事情,你似乎描述了不是这样吗?而且,你不需要使用任何与c有关的东西