12.内存泄漏-碎片.md 4.5 KB

内存泄漏 / 内存碎片

判别规则(核心,唯一要记的)

  • 判别泄漏malloc/calloc/realloc 必须找到对应的 free,且覆盖所有代码路径(提前 return 也算);指针被覆盖前先 free 原来的
  • 判别碎片 vs 泄漏:可用内存总量持续减少 = 泄漏;总量没少但大块申请失败 = 碎片

一句话:"配对不齐是泄漏,洞不齐是碎片;泄漏少总量,碎片乱布局。"

抽象描述(一句话本质)

内存泄漏 = 申请了不释放(该还没还,总量变少);内存碎片 = 释放了但空间不连续(还了但乱,总量够却申请不到大块)。泄漏是"忘还",碎片是"还了但乱"。

正反例(建立直觉)

  • 正例1(基本泄漏)void f(){ int *p = malloc(100); /* 忘 free */ } → 每次调用泄漏 100 字节。查法 = malloc/free 一一配对
  • 正例2(隐蔽泄漏)int *p = malloc(100); p = malloc(200); → 第一个 100 字节地址被覆盖丢了,没法 free → 泄漏。指针被重新赋值前必须先 free
  • 正例3(碎片):反复 malloc/free 不同大小 → 中间留洞,总量够但每洞太小 → 大块申请失败
  • 反例1(路径不匹配)int *p = malloc(100); if (flag) return; free(p); → 提前 return 漏了 free,也是泄漏(配对要覆盖所有路径)
  • 反例2(碎片≠泄漏):碎片时内存总量没少,只是不连续;泄漏时总量实实在在变少

易混对比

内存泄漏 内存碎片
本质 该还没还(总量变少) 还了但乱(总量够、不连续)
现象 可用内存持续减少 大块分配失败
原因 malloc 不配对 free 随机分配/释放不同大小
查法 malloc/free 配对检查 看分配失败时空闲块分布
治理 配对、RAII、内存池归还 合并算法、固定大小块、内存池

变体验证(3 题,全过=学会)

  1. int *p = malloc(100); p = malloc(200);泄漏(第一个 100 字节地址被覆盖丢失,没法 free)
  2. 陷阱题:malloc/free 换成内存池(固定大小块)→ 碎片问题解决(固定大小原样复用),但泄漏没解决(还是要靠归还配对)
  3. 陷阱题:嵌入式长期运行 → 碎片是渐变后突发(跑很久某次大申请突然失败);泄漏是线性累积、可监测(剩余内存持续减少)

口述要点(面试怎么讲)

  • 结论先行:泄漏少总量(该还没还),碎片乱布局(还了不齐)
  • 嵌入式为什么特别致命:PC 有虚拟内存(MMU)把物理散页翻译成连续虚拟地址,外部碎片被掩盖 + 内存大可换页,所以 PC 主要栽在泄漏;嵌入式重启代价高(产品不能随便重启)、内存小、程序不退出所以泄漏永久累积、崩溃只能看门狗复位带数据丢失
  • 检测手段:① FreeRTOS xPortGetFreeHeapSize() 看剩余堆反复减少 = 泄漏 ② 包装记账(my_malloc/my_free 计数器,申请数−释放数 ≠ 0 即泄漏)③ 记录调用点(带 __FILE__/__LINE__ 打印谁申请的)④ PC 开发期 valgrind / AddressSanitizer 预检
  • 治理碎片:① 合并相邻空闲块(coalescing)② 固定大小内存池(内存分区)③ best-fit / first-fit 分配策略 ④ 减少大小混杂的小对象频繁分配
  • 易错点:提前 return 漏 free;把悬垂(用了已释放)和泄漏(该还没还)混为一谈

关系网络

相邻概念 和本主题的关系 孤立理解会犯的错
堆 vs 栈 泄漏/碎片全发生在堆——栈天然没有这两个问题 以为栈也有泄漏
内存池 治理碎片的第一手段:固定大小块 + 归还配对 以为池子能顺带治好泄漏
野指针 悬垂(用了已释放)vs 泄漏(该还没还)——一对反向错误 把悬垂和泄漏混为一谈
FreeRTOS 堆管理 heap_1~heap_5:不同策略对应不同碎片/泄漏特性 以为 FreeRTOS 内存和裸机 malloc 一样

学习日期

2026-08-03


每主题一页,复习时只翻本目录。