前言
作为一个C程序员,那么内存泄漏不可避免,当排查自己的代码还好一些,但是排查起来别人的“狮山”或者一些开源代码,常常会让自己非常的崩溃,今天总结一下如何确认程序确实发生了内存泄漏以及定位手段。
确认内存泄漏手段
借助工具
LINUX平台中最出名的内存检测工具:Valgrind, 它会生成非常详细的报告,包括泄露的位置和分配的内存大小:
valgrind --leak-check=full ./your_program
观察内核数据
程序运行时发生内存泄漏会变的非常缓慢且占用很多的内存。
例如一个程序进程是4268, 通过命令cat /proc/4268/status
查看内存:
VmSize: 虚拟内存
VmRSS: 物理内存
RSS: 是常驻内存集(Resident Set Size),表示该进程分配的内存大小。
RSS: 不包括进入交换分区的内存。
RSS: 包括共享库占用的内存(只要共享库在内存中)
RSS: 包括所有分配的栈内存和堆内存。
VSZ: 表示进程分配的虚拟内存。
VSZ: 包括进程可以访问的所有内存,包括进入交换分区的内容,以及共享库占用的内存。
隔一段时间查看所占内存是否持续有增加,如果有增加证明有内存释放。
或者通过top
命令查看进程所占的内存。
当然,该方法不太准确,具体要根据程序的情况去判断,例如程序应用确实申请了内存,还没到释放的时候,那么观察到内存有增加,是正常的。
编写一个测试用例
创建一个测试用例,通过模拟不同的情况,观察是否会导致内存不断增加。
定位手段
借助工具
同样的,Valgrind 是常见的定位内存泄漏工具,其他的还有GDB、Xcode Instruments、Purify等。
观察
手动检查代码确认内存泄漏,虽然不如工具直观,方式如下:
-
在每次分配内存后,请确保有对应的释放操作(如free或delete)。检查代码中是否有忘记释放内存的地方。
-
使用计数器来跟踪内存分配和释放的次数。在每次分配时增加计数器,在每次释放时减少计数器。如果计数器最终不为零,那么可能存在内存泄漏。
-
在程序退出前,检查所有应该被释放的内存是否都被释放。这可以通过编写一个清理函数来实现。
-
追踪指针:跟踪指向已释放内存的指针,这可能是悬挂指针的一个来源。在释放内存后,将指针设置为NULL,以防止意外访问。
-
循环引用:如果你使用了复杂的数据结构,如链表或树,确保没有循环引用导致内存无法被释放。这可能需要仔细检查你的数据结构和释放逻辑。
代码优化
-
内存分配错误检查:确保内存分配操作成功,否则可能会导致未初始化的内存泄漏。在调用malloc或类似函数后,检查返回值是否为NULL。
-
内存释放位置:确认内存释放的位置在正确的地方,不要在循环中或不恰当的条件下释放内存。释放内存后,避免再次引用该内存。
-
记录内存分配和释放:添加日志或打印语句来跟踪内存分配和释放的情况,以便更容易定位问题。
-
内存池:考虑使用内存池来管理内存分配,以减少碎片和泄漏的风险。