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
相关产品推荐
相关产品推荐

