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

问题描述 投票:2回答:1

问题背景

从过去几个月我们在我的在线Asp.net应用程序中发现了问题。应用程序工作正常,但一天中有1或2次突然崩溃在实时服务器上的不同模块上,但它们在代码中根本没有这样的问题,并且在本地服务器上找不到此类问题。

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

临时解决方案:

当我们发现这样的问题时,我们重启IIS上的应用程序。这可以结束这个过程并开始新的过程,然后在该应用程序开始工作之后。

在某一天,我们需要重新启动您的应用程序2到3次。

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

经过一些研究后找到一些方案:

  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进行详细审查,我在服务器停止响应时获取此转储文件

asp.net debugging memory-leaks windbg
1个回答
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。尽管如此,这两个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内存映射文件。

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

© www.soinside.com 2019 - 2024. All rights reserved.