【问题标题】:Using Windbg to find Memory leak issue in asp.net Application使用 Windbg 查找 asp.net 应用程序中的内存泄漏问题
【发布时间】:2019-03-19 11:31:07
【问题描述】:

问题背景

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

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

临时解决方案:

当我们发现此类问题时,我们会在 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?

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

【问题讨论】:

标签: asp.net debugging memory-leaks windbg


【解决方案1】:

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

这不是一个好的选择,因为

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

使用 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

如您所见,有 3 个块进入了 VirtualAlloc。尺寸有点不切实际:

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

这将是 3.3TB + 1.7TB = 6TB 而不是 2TB。现在,这可能是!address的一个bug,但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.8MB 的相对较小的块。

最后一部分,直接调用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 的内存映射文件,它未被使用(因此被保留)。

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

【讨论】:

  • Thomas Weller 感谢您宝贵的时间,但我仍然无法找到解决方案。你能建议我正确的方法和技术来帮助我找到内存泄漏的解决方案吗?一些详细的教程可以详细解释我的问题的解决方案。一步一步引导一些事情。 ,在此先感谢。
  • @AliImran:没有“a”分步指南。正如您在图中看到的,在此过程中需要做出几个决定。因此,有几个分步指南,视情况而定。为了提供帮助,我需要问题中未提及的其他信息。以!address -summary开头。
  • Thomas Weller 再次感谢您的回复。我与您共享一个链接,其中包含我的转储文件。如果您查看它并指导我缺少什么,我真的很感激。我还更新了我的问题并分享了 !address -summary 的屏幕截图,也可能会调查它。再次感谢。 drive.google.com/file/d/1ok9mWXg3NEIt6boEMjfOx1wXEgmmzSOL/view
  • 托马斯·韦勒,对吗?我从服务器生成转储文件并在我的本地机器windbg中分析它。托马斯非常感谢你的帮助,我的问题还没有解决,但你的信息让我接近解决方案。我在很多方面仍然感到困惑。如果您不介意可以和我交谈,任何适合您的时间,如果您有问题,我可以理解。再次感谢您和您宝贵的时间。
  • @AliImran:是的,可以在服务器上创建转储文件,然后使用不同的机器进行分析。我什至会说这是“正常”方式,因为您在生产系统上没有调试工具。我曾经做过故障转储分析方面的咨询,但我不再做。我不能在工作时安排电话,而且我还有其他一些我喜欢花时间做的爱好。但是,如果您为 SO 准备了一个具体而简短的问题,我一定会在 windbg 标签中看到它。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-01-26
  • 1970-01-01
  • 1970-01-01
  • 2011-10-29
  • 2011-06-15
相关资源
最近更新 更多