原因

Rust 通常情况下, 不会有内存泄漏的. 毕竟是号称内存安全的语言. 如果不幸出现了. 那可以按以下思路排查

  • 使用了 unsafe 代码导致. 这种几乎是 99% 的原因
  • 其他情况还没遇到过 ^_^

示例代码

在迁移公司的一个项目到 Rust 体验的时候, 加了下以的代码

pub fn keep_topn_object<T>(list: &mut Vec<T>, max: usize) {
    if list.len() <= max {
        return;
    }
    unsafe {
        list.set_len(max);
    }
}

代码的本意是想将 list 变量只保留前 max 个元素.

发现过程

通过 wrk 来进行压测时, 通过 HTOP 发现 VIRTRES 不断增加.

定位代码

调试前, 修改下系统的设置

echo 0 > /proc/sys/kernel/yama/ptrace_scope

echo 0 > /proc/sys/kernel/kptr_restrict

然后 Rust 在编译时, 加上 debug 信息, 或者就直接以 cargo run 来运行也可. 如果是 --release 则加上以下配置

[profile.release]
debug=true
  1. 通过 pmap 定位内存地址. 假设你的进程 PID 为 1234 . 则通过 pmap -x 1234 > /tmp/mem.1
  2. 进行 wrk 压测
  3. 再次执行 pmap 定位内存地址. pmap -x 1234 > /tmp/mem.2
  4. 比较差异. diff /tmp/mem.1 /tmp/mem.2 . 查看差异明显的内存地址位置.假设是 7fc50043f000
  5. 查看上面起始地址的内存段. cat /proc/1234/maps ,找到上面 7fc50043f000 内存地址范围. 假设查到的是 7fc50043f000-7fc50044f000
  6. 通过 gdb attach 1234 链接到进程
  7. 执行 gdb 的命令 dump memory /tmp/pid1234.bin 0x7fc50043f000 0x7fc50044f000 (注意, 要是 0x 开头)
  8. 然后通过 strings /tmp/pid1234.bin | less 可以慢慢看到泄漏的内容

我这里的实际情况时, HTTP 请求的内容有一个 ID 标识, xxxxxxxx , 然后 strings /tmp/pid1234.bin | grep xxxxxxxx | wc -l 看到的行数跟请求数差不多, 可以发现到, 实际是这块相关的内存泄漏了, 而原因正是上面的 unsafe 代码导致的. 修改为

pub fn keep_topn_object<T>(list: &mut Vec<T>, max: usize) {
    if list.len() <= max {
        return;
    }
    list.truncate(max);
}

即可

参考资料