内存泄漏探测器工作原理

时间:2015-02-11 04:54:16

标签: c++ c memory-leaks

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

3 个答案:

答案 0 :(得分:60)

检漏仪有两种不同的工作方式。您可以将mallocfree的实现替换为可以在分配期间跟踪更多信息但不关心性能的实现。这类似于dmalloc的工作方式。一般情况下,任何malloc但不是free的地址都会泄露。

基本实现实际上非常简单。您只需维护每个分配及其行号的查找表,并在释放条目时删除该条目。然后当程序完成后,您可以列出所有泄漏的内存。困难的部分是确定应该释放分配的时间和地点。当有多个指向同一地址的指针时,这就更难了。

在实践中,您可能不仅需要单行号,而且还需要丢失分配的堆栈跟踪。

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

Valgrind主要用于检测底层指令,并可以跟踪给定内存地址何时没有更多引用。它可以通过跟踪地址分配来实现这一点,因此它不仅可以告诉您一块内存丢失,而且 时它会丢失。

C ++使两种类型的检漏仪都变得有点困难,因为它增加了newdelete运算符。技术上new可以是与malloc完全不同的内存来源。但是,在实践中,许多真正的C ++实现仅使用malloc来实现new,或者可以选择使用malloc而不是替代方法。

C ++这样的高级语言也倾向于使用其他更高级别的方式来分配std::vectorstd::list等内存。基本的泄漏检测器将分别报告由更高级别模式进行的许多分配。这比说整个容器丢失要有用得多。

答案 1 :(得分:19)

这是published technical paper on how our CheckPointer tool works.

从根本上说,它跟踪所有值(堆和堆栈)的生命周期,以及它们根据语言定义的类型的大小。这样,CheckPointer不仅可以查找泄漏,还可以查找数组外的绑定访问,即使对于堆栈中的数组,valgrind也不会这样做。

特别是,它分析源代码以查找所有指针使用。 (这本身就是一项任务)。

它跟踪每个指针的指针元数据,包括

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

它还跟踪每个对象的种类和位置,即是否 它是一个函数,全局,线程局部或局部变量,堆分配的内存或字符串文字常量:

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

所有这些跟踪都是通过将原始程序源转换为执行原始程序所执行的程序,并交错各种元数据检查或更新例程来完成的。生成的程序已编译并运行。如果元数据检查在运行时失败,则会提供回溯,其中包含失败类型的报告(无效指针,有效边界外的指针,......)

答案 2 :(得分:8)

这是标记的C和C ++,没有提到操作系统。这个答案适用于Windows。

C

Windows具有虚拟内存的概念。进程可以获得的任何内存都是虚拟内存。这是通过VirtualAlloc() [MSDN]完成的。您可以想象泄漏检测器在该函数上设置断点,并且无论何时调用它,它都会获取callstack并将其保存在某处。然后它可以为VirtualFree()[MSDN]做类似的事情。

然后可以识别差异并将其与已保存的callstack一起显示。

C ++

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

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

Screenshot: GFlags enabled for Notepad

在这种情况下,它将为C ++堆管理器调用保存最多50 MB的callstack信息。

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

一般概念

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