内存悄悄涨 8GB 找不到原因?用 jeprof 给 jemalloc 拍张快照就能定位
2026/9/15 20:08:14 网站建设 项目流程

内存悄悄涨 8GB 找不到原因?用 jeprof 给 jemalloc 拍张快照就能定位

【免费下载链接】jemalloc项目地址: https://gitcode.com/GitHub_Trending/je/jemalloc

上周有个服务在压测后常驻内存从 2GB 一路爬到 10GB,重启后一切恢复如初——典型的"内存去哪了"问题。如果程序链接的是 jemalloc(一个为高并发场景优化的 C 内存分配器),你可以用它的配套工具 jeprof 给运行中的进程拍一张"内存账单"快照,再用火焰图把占用大头的那几条调用栈直接揪出来。全文按真实排查流程走一遍:装好带分析功能的 jemalloc → 让程序吐出快照文件 → 读懂 jeprof 报告 → 画出内存火焰图 → 坐实泄漏,最后给出生产环境长期开 profiling 的代价与规避办法。

第 1 步:编译一个"会记账"的 jemalloc

普通的 malloc 只负责把内存分出去,而启用 profiling 的 jemalloc 会额外记录"谁、在哪一行、分了多少没还"。这一步只需要在 configure 时加一个开关:

git clone https://gitcode.com/GitHub_Trending/je/jemalloc cd jemalloc ./autogen.sh ./configure --enable-prof --prefix=/usr/local/jemalloc make -j4 && make install

编译出来的库里就带上了 jeprof 需要的采样记账能力,jeprof 本体则随make install一起放进bin/目录。验证一下:

/usr/local/jemalloc/bin/jeprof --version

💡 避坑:如果忘了--enable-prof,程序照样能跑,但 MALLOC_CONF 里写prof:true会被静默忽略,快照文件永远不会出现——这是新手最常见的"为什么没有输出"。可以用jemalloc-config --config | grep prof确认编译选项。

接着让目标程序替换掉系统自带的 libc malloc。最简单的方式是加链接参数:

gcc -g -o myapp myapp.c -L/usr/local/jemalloc/lib -ljemalloc -Wl,-rpath,/usr/local/jemalloc/lib

注意这里保留了-g。jeprof 要靠调试符号和 DWARF 信息把地址翻译成"文件:行号",不带-g的报告里只剩函数名,行号定位就没了。

第 2 步:让程序吐出快照文件(.heap 文件)

运行时通过环境变量告诉 jemalloc 开启记账,并指定账单落盘路径:

export MALLOC_CONF="prof:true,lg_prof_sample:20,prof_prefix:/tmp/prof/myapp" ./myapp

这三个参数的作用:

参数白话解释建议
prof:true打开记账总开关必须
lg_prof_sample:20平均每分配 2^20=1MB 随机抽一次样生产可调大到 22(4MB)降开销
prof_prefix快照文件名前缀指到进程有写权限的目录

采样是这里的关键机制:jemalloc 不是给每次分配都拍照,而是像路口装了一台"流量摄像头",按字节数概率性地随机抽拍,所以开销可控,代价是结果是统计值而非精确值(这正是它适合在生产环境跑的原因)。

程序正常退出时,jemalloc 会自动按前缀写出一个myapp.<pid>.<时间戳>.i<序号>.heap文件。中途想"抓拍"也可以不退出进程:

#include <jemalloc/jemalloc.h> char path[256]; mallctl("prof.dump", path, NULL, NULL, 0); // 立即生成一个 .heap 快照

⚠️ 避坑:快照文件由进程自己写盘,所以进程必须对prof_prefix目录有写权限,且目录所在磁盘要有剩余空间——权限不足时同样表现为"静默无文件"。

第 3 步:读懂 jeprof 文本报告

拿到.heap文件后,最常用的是--text(也叫 top 视图),按占用降序列出调用栈顶部函数:

jeprof --text /tmp/prof/myapp /tmp/prof/myapp.12345.1710000000.i1.heap

输出大致长这样:

Total: 8.2 G 0.551 46.2% 46.2% 0.551 46.2% parse_request_body 0.302 25.3% 71.5% 0.302 25.3% build_user_cache 0.089 7.4% 78.9% 0.089 7.4% serialize_json

四列数字是 jeprof 的固定口径:

  • flat:该函数自己直接分配的内存(不含它调用的下游);
  • flat%:flat 占总量百分比;
  • cumulative%:从根到它这一路的累计占比,用来判断"这条调用链整体有多重";
  • cumulative:该函数及其所有下游的总分配量。

看报告的技巧:先看 cumulative 找"重的调用链",再看 flat 找"真正动手分配的叶子函数"。上面例子里parse_request_body自己就吃了 46%,优化目标很明确。

两个常用变体:

# 精确到源码行号(需要 -g 编译) jeprof --text --lines /tmp/prof/myapp /tmp/prof/myapp.*.heap # 只看与某个函数相关的栈,比如排查 HTTP 请求路径 jeprof --text --focus=request /tmp/prof/myapp /tmp/prof/myapp.*.heap

第 4 步:一张内存火焰图看全局

文本报告适合看单点,但"整条调用链的重心在哪"用图更直观。jeprof 依赖 graphviz,装好后一条命令出图:

# Ubuntu/Debian 示例 sudo apt install -y graphviz jeprof --svg /tmp/prof/myapp /tmp/prof/myapp.*.heap > mem.svg # 想要 PDF 格式调用图则换成:jeprof --pdf ...

生成的 SVG 里,横轴宽度代表该栈帧占用的内存比例(越宽越重),纵轴是调用深度(父函数在下、被调方在上)。排查时的动作是:从根一路找最宽的"山脊",再沿着它往下钻到第一个明显变宽的业务函数,那就是优化入口。注意火焰图颜色只是用来区分相邻栈帧,没有深浅含义,别被误导。

💡 对比技巧:内存缓慢增长类问题,隔一段时间抓两次快照,用--diff_base=旧文件分析新文件,正号行表示这段时间净增的分配,增长最快的路径一眼可见。

第 5 步:坐实"内存泄漏"

前四步能告诉你"谁占了内存",但"泄漏"需要额外证据链,建议两步走:

  1. 抓基线:服务刚启动、流量平稳时 dump 一份.heap作为基线;
  2. 跑压力 + 二次对比:压测一小时后再 dump,用 diff 报告看持续净增的部分;同时开启泄漏模式:
MALLOC_CONF="prof:true,prof_leak:true,prof_prefix:/tmp/prof/leak"

prof_leak:true会让程序退出时额外输出一份"到死都没 free 的内存"清单。结合 mallctl 里的stats.allocated(当前在手上没还的字节数)观察趋势:allocated 随时间单调上涨且与业务数据量无关,基本可以定性为泄漏。

⚠️ 避坑:采样是随机的,小对象(几十字节)的泄漏可能在报告里被摊薄得几乎看不见。如果你怀疑的是小块高频泄漏,把lg_prof_sample调小(如 18,即 256KB 粒度)重采一次,但采样频率越高开销越大,两者要权衡。

生产环境长期开 profiling 的代价

直接把结论放前面:有开销,但可控,适合"默认低频率开着、出问题再加密"的策略。

  • CPU/吞吐开销:主要来自每次采样时的栈回溯(backtrace),量级通常在个位数百分比;lg_prof_sample每加 1(采样间隔翻倍),采样开销约减半,压测环境建议从 20(1MB)起步,大吞吐服务可放宽到 22 甚至更大。
  • 内存开销:每份被记录的分配栈都要缓存一份调用栈副本,采样粒度越细、存活分配越多,这笔"记账本"越大。
  • 磁盘开销.heap文件与存活分配规模正相关,注意监控prof_prefix所在分区容量,并给目录收紧权限(快照里含函数名,可能泄露内部模块结构)。
  • 动态开关:不必重启进程就能调整——运行时通过 mallctl 修改prof.active可临时开/关采样,lg_prof_sample也可热调,"平时关着、告警时打开"是常见的折中做法。

另外提醒一句:jeprof 输出的是统计采样结果,和 Valgrind 那种逐次分配精确跟踪不同,别拿它做"精确到某个字节"的泄漏举证;精确排查用它指路、再上针对性手段复核即可。

接下来往哪里看

仓库里的 doc/jemalloc.xml.in 是完整的 API 与所有prof.*配置项的官方文档源,参数语义以它为准;采样与记账的核心实现在 src/prof.c 和 src/prof_sys.c,想理解"为什么报告是长这样"可以从这两个文件读起。

【免费下载链接】jemalloc项目地址: https://gitcode.com/GitHub_Trending/je/jemalloc

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询