You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

tokio任务中使用mem::swap疑似内存泄漏,OOM问题排查求助

问题根源

既不是static也不是mem::swap,而是异步任务调度阻塞+Vec未及时清空导致的持续扩容:

  • main函数标记为异步,但内部loop是纯同步逻辑,没有任何await调度点,Tokio Runtime无法切换到tokio::spawn出来的consume任务。
  • consume里的clear操作根本没机会执行,Buffer中的Vec始终处于满状态。每次mem::swap后,main里的Vec变成了Buffer中满容量的Vec,继续push会触发Vec自动扩容(容量翻倍),循环往复导致内存持续增长最终OOM。
解决方法

针对核心问题,有两种可行的修复思路:

思路1:给main loop添加调度点,让Runtime有机会执行其他任务

在main的循环中加入tokio::task::yield_now().await,主动让出线程控制权,让Runtime调度consume任务执行:

#[tokio::main]
async fn main() {
    let mut vec: Vec<i64> = Vec::with_capacity(100);
    loop {
        vec.push(1);
        if vec.len() == 100 {
            let mut lk = Buffer.lock();
            mem::swap(&mut vec, &mut *lk);
            drop(lk);
            tokio::spawn(async move {
                consume().await
            });
        }
        // 主动让出控制权,让其他任务有机会执行
        tokio::task::yield_now().await;
    }
}

思路2:直接在main中同步执行清空逻辑,避免异步调度问题

既然consume的逻辑非常简单,不需要异步执行,可以直接在swap之后同步清空Buffer,省去spawn任务的开销:

#[tokio::main]
async fn main() {
    let mut vec: Vec<i64> = Vec::with_capacity(100);
    loop {
        vec.push(1);
        if vec.len() == 100 {
            let mut lk = Buffer.lock();
            mem::swap(&mut vec, &mut *lk);
            // 直接在这里清空,无需异步任务
            lk.clear();
            drop(lk);
        }
    }
}

额外优化:按需释放Vec容量

如果希望彻底释放内存,而不仅仅是清空元素,可以在clear后调用Vec::shrink_to_fit(),将Vec的容量缩减到匹配当前长度(0):

pub async fn consume() {
    let mut lk = Buffer.lock();
    lk.clear();
    lk.shrink_to_fit(); // 释放多余容量
    drop(lk);
}

内容的提问来源于stack exchange,提问作者YNX

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.19 12:55:25