必须加-g编译,否则valgrind只能显示汇编地址;-g提供dwarf调试信息以映射内存地址到源码行号,配合-o0避免优化干扰行号定位,cmake需设为debug模式并确保含-g标志。

必须加 -g 编译,否则 valgrind 只能显示汇编地址
Valgrind 本身不读源码,它靠可执行文件里嵌入的调试信息(DWARF)把内存地址映射回 sample.c:8 这样的位置。没有 -g,它连哪一行都猜不出来,只能给你一串十六进制地址,比如 0x4011AB。
- 用
gcc -g -O0 sample.c -o sample或g++ -g -O0 main.cpp -o main编译,-O0避免优化打乱行号映射 - 不要用
-O2或-O3,优化后变量可能被内联、删除或重排,valgrind 报的行号会偏移甚至错乱 - 如果项目用 CMake,确保
CMAKE_BUILD_TYPE是Debug,且CMAKE_CXX_FLAGS_DEBUG包含-g - 检查是否真嵌入了调试信息:
file ./sample输出里应有with debug_info;或者readelf -S ./sample | grep debug能看到.debug_*段
报错里有地址但没行号?试试 addr2line
即使漏掉了 -g,你也能手动补救:valgrind 输出里的地址(如 0x4011AB)可以交给 addr2line 反查源码位置。
- 先确认符号表还在:
nm -C ./sample | grep fun,如果函数名是U(undefined)或全是0000000000000000 T,说明符号被 strip 过,addr2line也无能为力 - 运行:
addr2line -e ./sample -f -C 0x4011AB,-f显示函数名,-C解析 C++ 符号名 - 注意:地址要对齐到函数入口,有时需减去偏移量;最好配合
objdump -d ./sample | grep -A10 '<fun>'</fun>看反汇编确认
--leak-check=full 和 --show-leak-kinds=all 必须配对用
只开 --leak-check=full 不够,它默认只报 definitely lost,而很多泄漏在间接引用链里,会被标为 possibly lost 或 still reachable,直接忽略就漏检了。
-
--leak-check=full提供调用栈和分配点,但不分类;--show-leak-kinds=all才强制列出全部四类:definitely、indirectly、possibly、still reachable - 常见误写:
--leak-check=full --show-leak-kinds=definite—— 拼错单词或少字母,valgrind 会静默忽略该参数,不报错也不生效 - 完整命令示例:
valgrind --tool=memcheck --leak-check=full --show-leak-kinds=all ./sample
为什么 valgrind 有时“没报错”,但内存就是涨?
Valgrind 只在程序退出时做最终扫描,运行中不实时预警。如果你的程序是长期运行的服务(比如 daemon),或泄漏发生在某个循环里但进程没退出,valgrind 就不会触发泄漏报告。
- 它不监控 RSS 增长,也不提供类似
top的实时视图;Massif工具能看堆内存快照,但得主动指定--tool=massif并用ms_print解析输出 - 对已运行的进程无法 attach,必须重启程序并带 valgrind 启动;没有类似
gdb attach的热检测能力 - 多线程下,若泄漏发生在某个线程 malloc 后立即被其他线程 free,但调用栈跨线程,valgrind 可能归类为
still reachable,容易误判为“安全”
真正难的不是跑出报告,而是读懂报告里 indirectly lost 后面那一长串指针跳转路径——那里藏着最隐蔽的泄漏根因,别光盯着第一行 malloc 就动手删。











