排查 Rust 中的内存泄漏方法

问题描述

我们的前端灰度服务在每次发布之后,占用的内存会一直增长,最终耗尽所有内存从而导致 OOM。

内存泄漏的场景

  • 循环引用:如果多个对象互相引用,导致它们无法被释放。
  • 不正确地使用Box或Rc:这些类型可以用于管理内存,但如果使用不当,也会导致内存泄漏。
  • 忘记释放资源:确保所有分配的内存,文件,网络连接等资源都得到了及时释放。

排查工具

tokio-console

PageServer 使用了 tokio 这个异步编程库。tokio-console 是一个用于审查 tokio 运行时的工具,可以帮助我们查看异步任务的运行状态。参考文档添加相关的代码来引入。开发者启动 tokio-console 命令便可以查看异步任务的状态了。

# 安装 & 引入相关初始化代码
cargo install --locked tokio-console
# 默认链接端口为 6669
tokio-console http://127.0.0.1:5555

jemalloc

既然没有出现异步任务的泄漏,那么接下来,只能去观察内存是如何分配的,那就必须要要使用到 jemalloc 了。需要在项目中引入这两个依赖:

tikv-jemallocator: 替换默认的内存分配器
jemalloc_pprof: 提供了导出 profile 文件的工具函数

将导出 profile 文件的逻辑包装成一个 http 接口,那么就可以运行过程中导出 profile 文件。在启动程序前,设置 export _RJEM_MALLOC_CONF=prof:true,lg_prof_interval:28 用以开启导出 profile 相关的功能。
jeprof 这款 cli 用来分析 profile 文件。当然也可以用 flamegraph.pl 来生成火焰图。

# 导出 profile 文件,并使用 jeprof 来分析
dump-profile() {
  curl http://127.0.0.1:3000/debug/profile/prof > "$1.prof"
  jeprof --svg ./target/release/page-server "$1.prof" > "$1.svg"
  # 导出成火焰图, flamegraph.pl 来自于 https://github.com/brendangregg/FlameGraph/blob/master/flamegraph.pl
  jeprof ./target/release/page-server "$1.prof" --collapse | perl flamegraph.pl > "$1.flamegraph.svg"
}

dump-profile 1

k6

其实 k6 通常被当做压测工具来使用。不过借助它可以很方便的模拟出线上的请求。它的一个优点是可以通过 JavaScript 编写测试脚本。简单调整配置就可以模拟出线上的场景,对 前端 非常友好。
在安装完成了 k6 cli 后,使用 k6 new 生成测试脚本,执行 k6 run script.js 来执行测试。

import http from 'k6/http';
import { sleep } from 'k6';

export const options = {
  // 同时请求的数量
  vus: 10,
  // 此次测试持续时长
  duration: '30s',
};

export default function () {
  const headers = {
    host: `some-project.huolala.cn`,
    timeout: '1s',
  };
  http.get(`http://127.0.0.1:3001/${path}`, { headers });

  sleep(1);
}

排查思路

  1. 导出线上 profile 文件来分析
  2. 使用 k6 来模拟线上请求,找出最小复现 demo
  3. 定位问题所在,修复 bug

问题分析与解决

通过对 profile 文件的分析,我们发现有两处地方存在较高的内存占用。

转载自:

https://juejin.cn/post/7477767063705337883 有实际案例建议观看