必须显式启用--leak-check=full才能报告内存泄漏详情,否则仅显示“in use at exit”摘要;需配合-g编译、--show-leak-kinds=all(或definite)、--trace-children=yes(子进程)及grep definitely lost定位源头。

–leak-check=full 必须显式启用
默认情况下,valgrind --tool=memcheck 不会报告内存泄漏详情,只做基础运行监控。不加 --leak-check=full,哪怕有 100MB 泄漏,LEAK SUMMARY 也只会显示 in use at exit: X bytes in Y blocks,没有调用栈、没有文件行号。
必须显式加上这个参数,Memcheck 才会逐块扫描未释放内存,并尝试回溯分配点:
-
--leak-check=full:显示每一块泄漏内存的完整调用栈(含源码文件和行号) -
--show-leak-kinds=all:同时显示definitely lost、possibly lost、still reachable等所有类别(仅definitely lost是真泄漏,其余需结合业务判断) - 若只关心明确泄漏,可简化为
--leak-check=full --show-leak-kinds=definite
没加 -g 编译?调用栈全是 ???:0
即使开了 --leak-check=full,如果程序编译时没带 -g,输出里所有地址都会显示为 ???:0 或只有函数名(如 malloc),无法定位到 my_list_add() 第 42 行 malloc 了但没 free。
正确做法是:
- 编译时务必加
-g:g++ -g -O0 -o app app.cpp(-O0可选,但优化可能内联函数,影响栈追踪) - 静态库也要重新用
-g编译,否则其内部泄漏点仍无行号 - 动态库若无调试符号,Valgrind 会跳过其源码信息——此时只能靠日志 + 地址偏移反查
泄漏点藏在子进程里?得开 --trace-children=yes
如果主程序 fork 出子进程,且泄漏发生在子进程中(比如守护进程模型、后台任务线程派生的子进程),默认 Valgrind 只监控主进程,子进程的 malloc 不会被 Memcheck 拦截。
要覆盖全部,必须加参数:
-
--trace-children=yes:让 Valgrind 跟进每个fork出来的子进程 - 注意:这会让日志量暴增,建议配合
--log-file分离输出,或用--child-silent-after-fork=yes抑制子进程非错误日志 - 若子进程很快退出,泄漏可能来不及被统计——需确保它存活到主进程结束前
输出太长找不到关键信息?优先 grep definitely lost
Valgrind 日志动辄上万行,真正有用的泄漏定位信息集中在 definitely lost 块的报错段。别从头翻,直接用命令过滤:
valgrind --tool=memcheck --leak-check=full ./app 2>&1 | grep -A 10 "definitely lost"- 或者先存日志:
valgrind --log-file=valgrind.log --tool=memcheck --leak-check=full ./app,再grep -A 15 "definitely lost" valgrind.log - 每条
definitely lost后紧跟的at 0x...: ... (xxx.cpp:123)就是泄漏源头,不是 free 的位置,是 malloc/new 的位置
最常被忽略的是:泄漏本身不报错,只在进程退出时汇总;而 Invalid read 或 Use of uninitialised value 这类运行时错误才是立即中断的。别等 crash 才想起看 Valgrind —— 它的泄漏检测只发生在 exit 阶段。











