Valgrind 是 Linux 下最強大的動態分析工具套件,Memcheck 是其核心組件,專門檢測內存錯誤。本文提供從安裝到實戰的完整指南。
# Ubuntu/Debian sudo apt-get install valgrind # CentOS/RHEL sudo yum install valgrind # 編譯需調試信息 gcc -g -O0 -o myapp myapp.c # 基本運行 valgrind --leak-check=full --show-leak-kinds=all ./myapp
| 錯誤類型 | 說明 | 嚴重程度 |
|---|---|---|
| Invalid read/write | 讀寫未分配/已釋放/棧外內存 | 致命 |
| Use after free | 釋放後訪問 | 致命 |
| Memory leak | malloc 無對應 free | 嚴重 |
| Invalid free | 雙重釋放、釋放非堆內存 | 致命 |
| Uninitialised value | 使用未初始化變量 | 嚴重 |
| Stack/Global overflow | 棧/全局區溢出 | 致命 |
==12345== 100 bytes in 1 blocks are definitely lost in loss record 1 of 1 ==12345== at 0x4C2DB8F: malloc (vg_replace_malloc.c:299) ==12345== by 0x4005F3: main (test.c:10) ==12345== Address 0x5203040 is 0 bytes inside a block of size 100 alloc'd ==12345== at 0x4C2DB8F: malloc (vg_replace_malloc.c:299) ==12345== by 0x4005F3: main (test.c:10)
→ 代碼第 10 行 malloc 未 free,必須修復。
==12345== 200 bytes in 1 blocks are indirectly lost ==12345== at 0x4C2DB8F: malloc ==12345== by 0x400621: create_node (list.c:45) ==12345== by 0x4005F3: main (test.c:12)
→ 結構體內指針指向的內存未釋放,需遞歸釋放。
==12345== Conditional jump or move depends on uninitialised value(s) ==12345== at 0x400645: process (test.c:20) ==12345== by 0x4005F3: main (test.c:15) ==12345== Uninitialised was created by a stack allocation ==12345== at 0x400610: process (test.c:18)
→ 變量未初始化即用於條件判斷,需初始化。
void free_list(Node *head) {
while (head) {
Node *next = head->next;
free(head->data); // 先釋放數據
free(head); // 再釋放節點
head = next;
}
}
char *build_string(const char *fmt, ...) {
va_list ap;
va_start(ap, fmt);
int len = vsnprintf(NULL, 0, fmt, ap);
va_end(ap);
char *buf = malloc(len + 1);
if (!buf) return NULL;
va_start(ap, fmt);
vsnprintf(buf, len + 1, fmt, ap);
va_end(ap);
return buf; // 調用者負責 free
}
void safe_free(void **ptr) {
if (ptr && *ptr) {
free(*ptr);
*ptr = NULL; // 防止二次釋放
}
}
#include#include // 程序中標記內存 VALGRIND_MALLOCLIKE_BLOCK(ptr, size, rz, is_zero); VALGRIND_FREELIKE_BLOCK(ptr, rz); // 檢查定義性 if (VALGRIND_CHECK_MEM_IS_DEFINED(ptr, size)) { ... } // 輸出當前洩露摘要 VALGRIND_DO_LEAK_CHECK; VALGRIND_PRINT_BACKTRACE;
# GitHub Actions 示例
- name: Run Valgrind
run: |
valgrind --leak-check=full \
--error-exitcode=1 \
--show-leak-kinds=definite,indirect \
./test_binary
# 任何 definite/indirect leak 導致非零退出碼,CI 失敗
| 局限 | 補充方案 |
|---|---|
| 運行速度慢 20-50x | 單元測試階段運行,非生產環境 |
| 內存佔用大 3-10x | 小規模測試數據 |
| 難以檢測邏輯錯誤 | 配合單元測試、靜態分析 |
| 不支持某些架構 | AddressSanitizer (ASan) 2-3x 慢,編譯期插桩 |