使用Windbg在asp.net应用程序中查找内存泄漏问题

时间:2019-03-19 11:31:08

标签: asp.net debugging memory-leaks windbg

  

问题背景

在过去的几个月中,我们在我的在线Asp.net应用程序中发现了问题。应用程序正常运行,但一天运行1到2次,却突然在实时服务器上的不同模块上崩溃,但是它们根本不是代码中的此类问题,在本地服务器上也找不到此类问题。

经过一些研究,我发现在实时服务器上的IIS上运行我的应用程序的进程的内存不断增加,并且当达到某个级别时,它开始崩溃。

  

临时解决方案:

一旦发现此类问题,我们将在IIS上重新启动应用程序。 可以结束此过程并启动新过程,然后在该应用程序开始运行之后。

一天之内,我们需要重新启动应用程序两次或更多次。

  

我发现的问题:内存泄漏。

     

经过研究后找到解决方案:

  1. 当应用程序崩溃时,从任务管理器的应用程序进程中创建转储文件。
  

工具使用:Windbg

  1. 在Windbg工具中打开进行分析。
  2. 写入命令

     .load by clr
      dumpheap -stat
    

显示了大量数据类型的引用。 现在我停留在这些地方。 我在图片部分与您分享。

  

问题:

1. I am on the right direction in finding memory leaks issue?
2. if my path is right where whats my next step?
3. Windbg tool is good for finding such kind of issue?

Output of dumpheap stack

enter image description here

dump file link进行详细检查,当服务器停止响应时,我将使用此转储文件

1 个答案:

答案 0 :(得分:0)

  

当应用程序崩溃时,从任务管理器的应用程序进程中创建转储文件

那不是一个好选择,因为

  • 您没有太多时间这样做。只要显示崩溃对话框,您就可以这样做。如果太晚了,该应用程序就不见了。
  • 在这种状态下,您将很难调试它。它会显示一个断点,而不是原始的异常,操作系统将使用该断点来显示对话框并收集诊断数据

使用WER本地转储在崩溃时自动创建故障转储,而无需手动进行。它更加可靠,并为您提供了原始的例外。参见How do I take a good crash dump for .NET

  

在寻找内存泄漏问题时我处于正确的方向吗?

对不起,您已经走错了轨道。

!dumpheap -stat开始并不是一个好主意。通常是从最低级别!address -summary开始。它将为您指示是托管内存泄漏还是本机内存泄漏。如果是托管泄漏,则可以继续进行!dumpheap -stat

  

如果我的路径正确,下一步该怎么做?

即使这不是正确的路径,也最好了解一下如何找出错误的路径。那么,我怎么知道?

看一下!dumpheap -stat的输出,您可以看到

[...]
111716     12391360 System.String.

这告诉您有110.000个不同的字符串,使用12 MB的内存。它还告诉您,其他所有内容都少于12 MB。查看其他大小,您会发现.NET并非导致OutOfMemoryException的原因。它们使用的内存少于50 MB。

如果存在托管泄漏,则将查找对象连接到的路径,以便垃圾收集器认为无法释放它。该命令为!gcroot

  

Windbg工具是否适合发现此类问题?

有可能,但是WinDbg并不是最好的工具。请改用内存分析器。那是用于内存泄漏的专用工具。通常,它具有更好的可用性。不幸的是,您需要决定是否需要托管内存分析器,本机内存分析器或两者。

我曾经写过how to use WinDbg to track down .NET OutOfMemoryException。您会在此处找到一个图表,该图表为您提供了在不同情况下如何进行操作的想法。


在您的转储中,我看到2 TB的<unknown>内存,可能是.NET,但是是needn't be。尽管如此,这2 TB可能是导致OOM的原因,因为其余的大小不到350 MB。

由于clr在已加载的模块列表中,因此我们可以像您一样检查!dumpheap -stat。但是使用内存的对象并不多。

!eeheap -gc显示有8个堆(对应于您的计算机的8个处理器)用于并行垃圾回收。最大的单个堆为45 MB,总计249 MB。这与!dumpheap的总和大致匹配。结论:.NET并非罪魁祸首。

让我们检查一下特殊情况:

  1. MSXML的存在
  2. 位图
  3. HeapAlloc()的调用太大,以至于它们直接转发到VirtualAlloc()
  4. 直接呼叫VirtualAlloc()

不存在MSXML:lm m msxml*不产生输出。

没有位图:!dumpheap -stat -type Bitmap

大于512 kB的堆分配:!heap -stat。这是输出的截断部分:

0:000> !heap -stat
_HEAP 0000018720bd0000
     Segments            00000006
         Reserved  bytes 0000000001fca000
         Committed bytes 0000000001bb3000
     VirtAllocBlocks     00000002
         VirtAlloc bytes 00000312cdc4b110
_HEAP 0000018bb0fe0000
     Segments            00000005
         Reserved  bytes 0000000000f0b000
         Committed bytes 0000000000999000
     VirtAllocBlocks     00000001
         VirtAlloc bytes 0000018bb0fe0110

如您所见,VirtualAlloc有3个块。大小有点不现实:

0:000> ? 00000312cdc4b110
Evaluate expression: 3379296514320 = 00000312`cdc4b110
0:000> ? 0000018bb0fe0110
Evaluate expression: 1699481518352 = 0000018b`b0fe0110

总共是3.3TB + 1.7TB = 6TB,而不是2TB。现在,这可能是!address的错误,但是4TB并不是常见的溢出点。

使用!heap -a 0000018720bd0000,您可以看到2个虚拟分配:

Virtual Alloc List:   18720bd0110
    0000018bac70c000: 00960000 [commited 961000, unused 1000] - busy (b), tail fill
    0000018bad07b000: 00960000 [commited 961000, unused 1000] - busy (b), tail fill

使用!heap -a 0000018bb0fe0000,您可以看到第三个:

Virtual Alloc List:   18bb0fe0110
    0000018bb1043000: 00400000 [commited 401000, unused 1000] - busy (b), tail fill

这些都是4.1MB和9.8 MB的较小块。

最后一部分,直接调用VirtualAlloc(),您需要回到!address的级别。使用!address -f:VAR -c:".echo %1 %3",您可以查看所有<unknown>区域的地址和大小。您会在其中找到很多条目,很多条目很小,有些条目可能是.NET堆,有些是2GB,还有一个很大的分配

2GB的存储空间:

0x18722070000 0x2d11000
0x18724d81000 0x7d2ef000
0x187a2070000 0x2ff4000
0x187a5064000 0x7d00c000
0x18822070000 0x2dfe000
0x18824e6e000 0x7d202000
0x188a2070000 0x2c81000
0x188a4cf1000 0x7d37f000
0x18922070000 0x2d13000
0x18924d83000 0x7d2ed000
0x189a2070000 0x2f5a000
0x189a4fca000 0x7d0a6000
0x18a22070000 0x2c97000
0x18a24d07000 0x7d369000
0x18aa2070000 0x2d0c000
0x18aa4d7c000 0x7d2f4000

这些可能是.NET堆(承诺部分+保留部分)。

大个子:

0x7df600f57000 0x1ffec56a000

有关它的信息:

0:000> !address 0x7df600f57000 

Usage:                  <unknown>
Base Address:           00007df6`00f57000
End Address:            00007ff5`ed4c1000
Region Size:            000001ff`ec56a000 (   2.000 TB)
State:                  00002000          MEM_RESERVE
Protect:                <info not present at the target>
Type:                   00040000          MEM_MAPPED
Allocation Base:        00007df5`ff340000
Allocation Protect:     00000001          PAGE_NOACCESS

它看起来像一个2TB内存映射文件,未使用(因此已保留)。

我不知道您的应用程序在做什么。这确实是我需要停止分析的地方。希望对您有所帮助,您可以得出结论并解决问题。