调查内存泄漏

我们在应用程序中有一个缓慢的内存泄漏,我已经通过以下步骤来分析泄漏的原因:

  1. 在GFlags中启用用户模式堆栈跟踪数据库
  2. 在Windbg中,键入以下命令: !heap -stat -h 1250000 (其中, 1250000是具有泄漏的堆的地址)比较多个转储后,我看到大小为0xC的存储器块随着时间而增加,内存泄漏。
  3. input以下命令: !heap -flt sc给出UserPtr的这些分配,最后:
  4. 在这些地址上inputheap -p -a地址总是显示下面的分配调用堆栈:

0:000> !堆-p -a 10576ef8

address 10576ef8 found in _HEAP @ 1250000 HEAP_ENTRY Size Prev Flags UserPtr UserSize - state 10576ed0 000a 0000 [03] 10576ef8 0000c - (busy) mscoreei!CLRRuntimeInfoImpl::`vftable' 7c94b244 ntdll!RtlAllocateHeapSlowly+0x00000044 7c919c0c ntdll!RtlAllocateHeap+0x00000e64 603b14a4 mscoreei!UtilExecutionEngine::ClrHeapAlloc+0x00000014 603b14cb mscoreei!ClrHeapAlloc+0x00000023 603b14f7 mscoreei!ClrAllocInProcessHeapBootstrap+0x0000002e 603b1614 mscoreei!operator new[]+0x0000002b 603d402b +0x0000005f 603d5142 mscoreei!GetThunkUseState+0x00000025 603d6fe8 mscoreei!_CorDllMain+0x00000056 79015012 mscoree!ShellShim__CorDllMain+0x000000ad 7c90118a ntdll!LdrpCallInitRoutine+0x00000014 7c919a6d ntdll!LdrpInitializeThread+0x000000c0 7c9198e6 ntdll!_LdrpInitialize+0x00000219 7c90e457 ntdll!KiUserApcDispatcher+0x00000007 

这看起来像线程初始化调用堆栈,但我需要知道比这更多。 下一步,你会build议做什么,以指出在泄漏的确切原因。

使用GFlags时记录的堆栈没有使用.pdb,通常不正确。 由于您已经将泄漏降至特定堆的特定大小,因此您可以尝试在RtlAllocateHeap中设置实时中断,并使用正确的符号在windbg中检查堆栈。 我已经使用了下面的一些成功。 您必须编辑它以适应您的堆和大小。

  $$ Display stack if heap handle eq 0x00310000 and size is 0x1303 $$ ==================================================================== bp ntdll!RtlAllocateHeap "j ((poi(@esp+4) = 0x00310000) & (poi(@esp+c) = 0x1303) )'k';'gc'" 

也许你会得到另一个堆栈和其他想法的罪犯。

首先,新运算符是new []运算符,那么是否存在相应的delete[]调用,而不是普通的旧delete调用?

如果你怀疑这个代码,我会在它周围放一个测试工具,例如把它放在一个循环中,执行它100或1000次,它是否仍然会泄漏,并且是成比例的。

您还可以使用进程资源管理器或使用GetProcessInformation以编程方式测量内存增加。

另一个显而易见的事情是看看当你注释掉这个函数调用时会发生什么,内存泄漏是否消失? 如果可能的话,您可能需要执行二进制排序,以便每次通过注释代码来减少可能的可疑代码的一半(粗略),但是,更改代码的行为可能会导致更多的问题或相关的代码路径问题导致内存泄漏或奇怪的行为。

编辑当您在托管环境中工作时,请忽略以下内容。

你也可以考虑使用STL或者更好地提高引用计数的指针,比如shared_ptr或scoped_array,用于数组结构来管理对象的生命周期。