C 语言内存泄漏入门
更新时间:2026-08-26。本文是
languages/c/主题入门层第 54 篇。内存泄漏不是"内存坏了",而是"你借了没还"——malloc 出来的内存没有 free,程序再也无法使用它。小程序泄漏无所谓,重启就好;服务器连续跑几个月,泄漏就是慢性自杀:内存一天天涨,最后 OOM 被杀。
本文要回答的问题
- 什么算内存泄漏?泄漏会怎样?
- 最常见的泄漏场景有哪些?
- 怎么用工具把它揪出来?
一、什么是内存泄漏
c
void handle_request(void) {
char *buf = malloc(4096);
// ... 处理请求 ...
// 忘了 free(buf)
}每次请求进来,buf 的 4KB 就"人间蒸发"。指针变量 buf 在函数返回后没了,但堆上的内存还在——没有指针指向它,谁也释放不了。
泄漏 = 失去对已分配内存的引用,且从未释放。判断标准两条:① 还持有指针?② 释放了吗?两者都不满足就是泄漏。
二、泄漏的三种典型场景
1. 忘写 free(最常见):
c
char *s = malloc(128);
sprintf(s, "%d", x);
return s; // 调用方忘记 free,或调用路径出错2. 指针被覆盖:
c
int *p = malloc(100 * sizeof(int));
p = malloc(200 * sizeof(int)); // 第一次分配的地址丢了!第一次的 100 个 int 再也没人知道在哪,无法释放。
3. 提前 return / 错误分支漏掉:
c
char *line = malloc(256);
if (read_ok(line) == 0) {
return; // ❌ 这里漏了 free(line)
}
free(line);错误处理路径最容易被忽略——每个出口都要释放。
三、危害:为什么服务器最怕泄漏
| 场景 | 影响 |
|---|---|
| 一次性小程序 | 退出时 OS 回收,无感 |
| 长期运行的服务 | 内存持续增长 → swap → OOM 被杀 |
| 循环/高频路径 | 泄漏速度 × 频率,几小时撑爆 |
观察方式:top / htop 里 RES 只涨不跌;/proc/<pid>/status 的 VmRSS 单调上升。配合内存监控可以确认。
四、工具定位:Valgrind 与 ASan
Valgrind(最经典):
bash
gcc -g -o prog leak.c
valgrind --leak-check=full ./prog输出会明确列出:泄漏块地址、大小、分配时的调用栈——一眼看到是哪个函数漏的。
==12345== 4096 bytes in 1 blocks are definitely lost in loss record 1 of 1
==12345== at 0x...: malloc
==12345== by 0x...: handle_request (leak.c:8)ASan(AddressSanitizer,快、需要 -fsanitize):
bash
gcc -g -fsanitize=address -o prog leak.c
./prog # 泄漏检测在退出时报告配合 ASAN_OPTIONS=detect_leaks=1 默认开启。完整用法见ASan 内存检测与Valgrind 内存检测。
五、防泄漏的工程习惯
- 谁分配谁释放:文档写明"返回值需调用方 free",调用方负责;
- 函数内多出口用 goto cleanup:
c
char *a = malloc(64);
char *b = malloc(64);
if (fail1) goto out; // 统一出口
if (fail2) goto out;
// ...
out:
free(a);
free(b);
return; // 不会漏- 封装 RAII 风格:创建/销毁成对函数(
vec_create/vec_free); - 提交前跑一遍 Valgrind/ASan,让工具当守门员。
六、常见坑对照
| 坑 | 现象 | 对策 |
|---|---|---|
| 错误分支漏 free | 偶发泄漏 | goto cleanup 统一出口 |
| 指针二次赋值 | 地址丢失 | 覆盖前先 free |
| 忘判 realloc 结果 | 原指针被覆盖 | 临时变量 |
| 库函数返回需释放的内存 | 不知道要 free | 查文档约定 |
| 只在测试时泄漏 | 线上爆掉 | 常跑泄漏检测 |
七、与本站主线衔接
- 越界/双释/悬垂等其他内存错误,见内存错误排查;
- ASan 与 Valgrind 完整教程,见ASan 内存检测、Valgrind 内存检测;
- 长期服务内存监控,见free 与 vmstat。
一句话总结
内存泄漏 = malloc 后失去引用且从未释放,小程序无感、长期服务致命;三大场景(忘 free、指针被覆盖、错误分支漏掉)用"谁分配谁释放 + goto 统一出口 + Valgrind/ASan 常态化检测"来防——借了内存要记账,工具帮你查账。