内存泄漏检测工作原理

内存泄漏检测器如何实际工作? 一般的基本概念是什么? 可以用C ++作为语言来解释这一点。

检漏仪有几种不同的方法。 你可以将mallocfree的实现replace为可以在分配期间追踪更多信息的实现,而不关心性能。 这与dmalloc工作方式类似。 一般来说, malloc但不是' free '的任何地址都被泄露。

基本的实现其实很简单。 您只需维护每个分配和其行号的查找表,并在释放条目时删除条目。 然后当程序完成时,你可以列出所有泄漏的内存。 困难的部分是确定何时何地分配应该被释放。 当有多个指向相同地址的指针时更是如此。

在实践中,您可能不仅需要单个线路号码,而且还要为丢失的分配提供堆栈跟踪。

另一种方法是valgrind如何工作,它实现了整个虚拟机来跟踪地址和内存引用以及相关的簿记。 valgrind的方法要贵得多,但也更有效,因为它也可以告诉你其他types的内存错误,如越界读取或写入。

Valgrind实质上是testing底层的指令,并可以跟踪给定的内存地址何时没有更多的引用。 它可以通过跟踪地址分配来做到这一点,因此它可以告诉你不只是一个内存丢失,而是它丢失时。

对于这两种泄漏检测器,C ++使事情变得更加困难,因为它增加了newdelete操作符。 从技术上讲, new可以是一个完全不同于malloc的内存来源。 然而,在实践中,许多真正的C ++实现只是使用malloc来实现new或者有一个使用malloc而不是替代方法的选项。

像C ++这样的高级语言也倾向于使用更高层次的方式来分配内存,如std::vectorstd::list 。 一个基本的泄漏检测器将分别报告由较高水平模式进行的潜在的许多分配。 这比说整个容器丢失没有用处。

这是一篇关于CheckPointer工具如何工作的公开技术文章。

从根本上讲,它跟踪所有值(堆和堆栈)的生命周期,以及它们根据语言定义的types的大小。 这允许CheckPointer不仅能够发现泄漏,而且还能够发现数组外绑定的访问,即使对于堆栈中的数组,valgrind也不会这样做。

特别是,它分析了源代码来查找所有指针的用法。 (这本身就是相当的任务)。

它跟踪每个指针的指针元数据,由…组成

  • 对指针指向的堆分配对象或全局或局部variables或函数的对象元数据的引用
  • 指针可能当前访问的对象的(子)对象的地址范围。 这可能小于整个对象的地址范围; 例如,如果你取得一个结构成员的地址,那么在使用结果指针时,被检测的源代码将只允许访问那个成员。

它还跟踪每个对象的types和位置,即它是一个函数,全局variables,线程本地variables还是局部variables,堆分配内存或string常量:

  • 可以安全访问的对象的地址范围,以及
  • 对于存储在堆分配对象或variables中的每个指针,指向该指针的指针元数据。

所有这些跟踪都是通过将原始程序源代码转换成一个程序来完成的,该程序完成原始程序的function,并交织各种元数据检查或更新程序。 生成的程序被编译并运行。 在元数据检查在运行时失败的情况下,回溯被提供有失败types的报告(无效指针,有效边界之外的指针,…)

这是C和C ++的标签,没有提到操作系统。 这个答案是针对Windows的。

C

Windows有虚拟内存的概念。 任何进程可以获得的内存都是虚拟内存。 这是通过VirtualAlloc()[MSDN]完成的 。 您可以想象泄漏检测器在该函数上放置一个断点,每当它被调用时,它都会获取调用堆栈并将其保存到某个地方。 然后它可以做类似的VirtualFree()[MSDN] 。

然后可以识别这些差异并与已保存的调用堆栈一起显示。

C ++

C ++有一个不同的概念:它从VirtualAlloc()中获取大的64kb块,并将其分割成更小的块,称为Heap。 C ++堆pipe理器来自Microsoft,提供了新的方法HeapAlloc()[MSDN]和HeapFree()[MSDN] 。

然后,你可以像以前一样做,但实际上,该function已经内置。 微软的GFlags [MSDN]工具可以启用跟踪:

屏幕截图:为记事本启用GFlags

在这种情况下,它将为C ++堆pipe理器调用节省多达50 MB的callstack信息。

由于该设置也可以通过Windowsregistry启用,因此内存泄漏检测器可以轻松使用它。

一般的概念

正如你所看到的,一般的概念是跟踪分配和释放,比较它们并显示差异的调用堆栈。