title: valgrind内存检测 type: raw created: 2026-08-31
valgrind是一套强大的内存调试、性能分析和检测工具集。它通过在程序运行时拦截系统调用和内存操作,检测各种内存相关问题:
| 工具 | 说明 |
|---|---|
| memcheck | 内存错误检测(默认工具) |
| cachegrind | 缓存性能分析 |
| callgrind | 函数调用分析 |
| helgrind | 多线程竞争检测 |
| drd | 多线程数据竞争检测 |
| massif | 堆内存分析 |
| lackey | 指令计数 |
程序中malloc/new后未对应free/delete,且无任何指针指向该内存:
void direct_leak() {
char *p = malloc(100); // 直接泄漏:p未释放
// 缺少 free(p);
}
后果:程序运行期间内存持续占用,进程退出时由OS回收。
指针丢失导致整块内存无法访问:
void indirect_leak() {
int *arr = malloc(10 * sizeof(int)); // arr是直接泄漏
int *ptr = arr + 5; // ptr指向arr中间
free(arr); // 只释放了arr的起始地址
// ptr指向的内存泄漏
}
程序退出前未释放但指针仍可达的内存:
static char *global_ptr;
void reachable_leak() {
global_ptr = malloc(100);
// 函数结束时global_ptr仍可达
// 未释放但不影响程序运行
}
指针指向已释放内存区域中间位置:
void possible_leak() {
int *arr = malloc(10 * sizeof(int));
int *ptr = arr + 5;
free(arr);
// ptr指向已释放内存,valgrind报告可能泄漏
}
# 基本内存检测
valgrind --leak-check=full ./program
# 显示详细信息
valgrind -v --leak-check=full ./program
# 生成日志文件
valgrind --log-file=valgrind.log --leak-check=full ./program
# 追踪未初始化变量来源
valgrind --track-origins=yes --leak-check=full ./program
# 显示可到达的泄漏(默认隐藏)
valgrind --show-reachable=yes --leak-check=full ./program
# 显示所有泄漏(包括可修复的)
valgrind --show-possibly-lost=yes --leak-check=full ./program
# 检测双重释放
valgrind --check-stackrefs=yes ./program
# 指定错误上限
valgrind --errors-for-leak-kinds=definite --leak-check=full ./program
| 选项 | 说明 |
|---|---|
--leak-check=full |
完整泄漏检查 |
--leak-check=summary |
仅显示摘要 |
--leak-check=no |
不检查泄漏 |
--show-reachable=yes |
显示可到达的泄漏 |
--show-possibly-lost=yes |
显示可能的泄漏 |
--track-origins=yes |
追踪未初始化值来源 |
--error-exitcode=1 |
检测到错误时返回非零值 |
--log-file=FILE |
输出到日志文件 |
--suppressions=FILE |
使用抑制规则文件 |
--trace-children=yes |
跟踪子进程 |
--num-callers=N |
调用栈深度 |
==12345== Memcheck, a memory error detector
==12345== Copyright (C) 2002-2023, and GNU GPL'd, by Julian Seward.
==12345== Using Valgrind-3.21.0 and LibVEX; rerun with -h for copyright info
==12345== Command: ./program
==12345==
==12345== Invalid read of size 4
==12345== at 0x10917A: main (test.c:10)
==12345== Address 0x4a2a040 is 0 bytes after a block of size 40 alloc'd
==12345== at 0x4848899: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==12345== by 0x10916B: main (test.c:8)
==12345==
==12345==
==12345== HEAP SUMMARY:
==12345== in use at exit: 40 bytes in 1 blocks
==12345== total heap usage: 1 allocs, 0 frees, 40 bytes allocated
==12345==
==12345== 40 bytes in 1 blocks are definitely lost in loss record 1 of 1
==12345== at 0x4848899: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==12345== by 0x10916B: main (test.c:8)
==12345==
==12345== LEAK SUMMARY:
==12345== definitely lost: 40 bytes in 1 blocks
==12345== indirectly lost: 0 bytes in 0 blocks
==12345== possibly lost: 0 bytes in 0 blocks
==12345== still reachable: 0 bytes in 0 blocks
==12345== suppressed: 0 bytes in 0 blocks
==12345==
==12345== For lists of detected and suppressed errors, rerun with: -s
==12345== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 0 from 0)
| 字段 | 说明 |
|---|---|
Invalid read/write |
非法内存读写 |
definitely lost |
确定泄漏(无指针指向) |
indirectly lost |
间接泄漏(父指针丢失) |
possibly lost |
可能泄漏(指针指向块中间) |
still reachable |
可达但未释放 |
suppressed |
被抑制的错误 |
# 非法读取
Invalid read of size N
# 原因:访问已释放或未分配的内存
# 非法写入
Invalid write of size N
# 原因:写入已释放内存或越界
# 使用未初始化值
Conditional jump or move depends on uninitialised value(s)
# 原因:使用未初始化的变量
# 系统调用参数错误
Syscall param ... points to uninitialised byte(s)
# 原因:传递未初始化数据给系统调用
void memory_leak() {
int *p = malloc(100);
// 缺少 free(p);
}
void out_of_bounds() {
int arr[10];
arr[10] = 1; // 越界写入
}
void use_after_free() {
int *p = malloc(100);
free(p);
*p = 10; // 使用已释放内存
}
void double_free() {
int *p = malloc(100);
free(p);
free(p); // 双重释放
}
void uninitialized() {
int x;
if (x > 0) { // x未初始化
printf("positive\n");
}
}
void mismatch() {
int *p = new int;
free(p); // 应该用 delete
}
// 自定义内存跟踪器
#define TRACK_MALLOC(size) tracked_malloc(size, __FILE__, __LINE__)
#define TRACK_FREE(ptr) tracked_free(ptr)
static void *tracked_malloc(size_t size, const char *file, int line) {
void *p = malloc(size);
printf("MALLOC: %p at %s:%d\n", p, file, line);
return p;
}
static void tracked_free(void *p) {
printf("FREE: %p\n", p);
free(p);
}
// 固定大小内存池,避免频繁malloc/free
#define POOL_SIZE 1024
static char pool[POOL_SIZE];
static size_t pool_used = 0;
void *pool_alloc(size_t size) {
if (pool_used + size > POOL_SIZE) return NULL;
void *p = &pool[pool_used];
pool_used += size;
return p;
}
# 编译时启用
gcc -fsanitize=address -g -o program program.c
# 运行时自动检测
./program
# 设置环境变量
export MTRACE=/tmp/mtrace.log
./program
# 分析日志
mtrace ./program /tmp/mtrace.log
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct {
char *name;
int age;
} Person;
Person *create_person(const char *name, int age) {
Person *p = malloc(sizeof(Person));
if (!p) return NULL;
p->name = malloc(strlen(name) + 1);
if (!p->name) {
free(p);
return NULL;
}
strcpy(p->name, name);
p->age = age;
return p;
}
void process_data() {
int *buffer = malloc(100 * sizeof(int));
for (int i = 0; i < 100; i++) {
buffer[i] = i;
}
// 缺少 free(buffer);
}
void leak_example() {
Person *alice = create_person("Alice", 25);
Person *bob = create_person("Bob", 30);
printf("Alice: %s, %d\n", alice->name, alice->age);
printf("Bob: %s, %d\n", bob->name, bob->age);
// 缺少释放
// free(alice->name);
// free(alice);
// free(bob->name);
// free(bob);
process_data();
}
int main() {
leak_example();
return 0;
}
$ gcc -g -o leak_demo leak_demo.c
$ valgrind --leak-check=full --track-origins=yes ./leak_demo
==12345== Memcheck, a memory error detector
==12345== Command: ./leak_demo
==12345==
Alice: Alice, 25
Bob: Bob, 30
==12345==
==12345== HEAP SUMMARY:
==12345== in use at exit: 160 bytes in 4 blocks
==12345== total heap usage: 6 allocs, 2 frees, 520 bytes allocated
==12345==
==12345== 32 bytes in 2 blocks are definitely lost in loss record 1 of 2
==12345== at 0x4848899: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==12345== by 0x109205: create_person (leak_demo.c:12)
==12345== by 0x1092A8: leak_example (leak_demo.c:32)
==12345== by 0x1092F4: main (leak_demo.c:41)
==12345==
==12345== 128 bytes in 2 blocks are definitely lost in loss record 2 of 2
==12345== at 0x4848899: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==12345== by 0x109237: create_person (leak_demo.c:16)
==12345== by 0x1092A8: leak_example (leak_demo.c:32)
==12345== by 0x1092F4: main (leak_demo.c:41)
==12345==
==12345== LEAK SUMMARY:
==12345== definitely lost: 160 bytes in 4 blocks
==12345== indirectly lost: 0 bytes in 0 blocks
==12345== possibly lost: 0 bytes in 0 blocks
==12345== still reachable: 0 bytes in 0 blocks
==12345== suppressed: 0 bytes in 0 blocks
==12345==
==12345== For lists of detected and suppressed errors, rerun with: -s
==12345== ERROR SUMMARY: 2 errors from 2 contexts (suppressed: 0 from 0)
void leak_example_fixed() {
Person *alice = create_person("Alice", 25);
Person *bob = create_person("Bob", 30);
printf("Alice: %s, %d\n", alice->name, alice->age);
printf("Bob: %s, %d\n", bob->name, bob->age);
// 正确释放
free(alice->name);
free(alice);
free(bob->name);
free(bob);
process_data_fixed();
}
void process_data_fixed() {
int *buffer = malloc(100 * sizeof(int));
for (int i = 0; i < 100; i++) {
buffer[i] = i;
}
free(buffer); // 释放内存
}
--fair-sched=yes获得更准确的多线程结果// suppressions.supp
{
glibc_still_reachable
Memcheck:Leak
match-leak-kinds: reachable
...
fun:_dl_*
...
}
valgrind --suppressions=suppressions.supp ./program
valgrind如何检测内存泄漏?
definitely lost和possibly lost的区别?
valgrind的性能影响?
如何在生产环境使用?
valgrind能检测哪些错误?
valgrind和ASan的区别?
# 基本检测
valgrind --leak-check=full ./program
# 追踪未初始化值
valgrind --track-origins=yes ./program
# 生成日志
valgrind --log-file=valgrind.log ./program
# 抑制特定错误
valgrind --suppressions=supp.supp ./program
本文件用于知识归档,内容可直接用于学习与复习。