当程序出现类似内存泄露等内存不稳定的问题时,若无法从代码审核角度发现问题,则考虑跟踪内存的分配和释放。

从跟踪的数据中得到程序内存的使用分布情况,以及退出时没有释放的内存,可以推断出程序内存开销的热点,以及内存泄露位置,执行相应的改进。

##常规跟踪方法 linux下有跟踪内存泄露的工具,但工具操作和数据解读的不习惯,以及DIY内存跟踪的轻易性,实际的使用效果并不理想。

常规的跟踪方法一般是重载operator new operator delete ,使重载参数中包含文件名和行号,这样new/delete分配时会传递分配的文件位置。

operator new实现中,为每块请求的内存多分配出一个item结构,item结构中记录这次分配的大小和文件位置。

等到operator delete回收内存时,将指针偏移一个item结构的位置,就能读到item中的信息。

通过统计所有的item信息,得出内存使用分布,以及泄露的内存。

常规跟踪方法的限制 因为定义的 operator new operator delete 比标准方法增加了参数,需要定义new宏,并将new宏包含在每一个源文件中。

##优化出新的跟踪方法 新的方法仍然基于重载operator new operator delete,但是重载标准方法,不增加参数。那么如何记录每次分配的大小和文件位置呢?

我们预先定义好一个item元素的哈希表,key是一个整数。在 operator new 的实现里,调用系统接口 backtrace 获得分配内存的调用堆栈,计算这些堆栈的哈希key,然后将这些堆栈数据保存到哈希表的item元素中。调用堆栈等价于文件位置。

我们依旧需要为每块请求的内存多分配一个指针,指向item元素,以便在operator delete 的时候,修改item信息。

我们分析内存分布和泄露之前,需要取得程序的core。导出item信息中的堆栈值之后,通过在gdb中执行以下指令: info line * addr 获得每个堆栈的代码文件位置。addr 是堆栈值。

gdb调试时,新方法比常规方法计算量更小,也更容易汇总和分类。也不需要被每个源文件包含,并能影响到每个静态库模块 (动态库尚未确认)

此外,还可以应用的 malloc_hook 上,需要注意的是hook分配时,不能为每块请求的内存额外分配,管理方式与new不同,易引起崩溃。