[转] – 使用 .Net Memory Profiler 诊断 .NET 应用内存泄漏(方法与实践)

使用 .Net Memory Profiler .NET 用内存泄漏(方法与践)

文章分:.net 关键字: memory leak, .net, .net memory profiler, asp.net

          做过应断与化的朋友都知道内存泄漏和来的危害,对这种情况的分析和定位一般会比,尤其在 .NET/Java 用中,式的堆内存管理以及托管间纷复杂的引用系,使分析和定位问题更加复杂。本文以我的了解,尽量明了:

   

  1.  一种对 .NET/Java 托管内存类应用的内存泄漏分析和断方法;
  2. 使用 .Net Memory Profiler 工具一个真 ASP.NET 用中存在内存泄漏问题的分析、程作示例。

本文包括以下问题、不足:

   

  1. 本文以我的有理解写成,尤其是”方法”相的内容,个人在不同情况下会有不同的方式;
  2. 不是 .Net Memory Profiler 工具的全面解,践中所及的功能了定位里 ASP.NET 用中的问题。可参 .Net Memory Profiler 文档 。

.NET/Java 托管内存类应用的内存泄漏分析和断方法

   

          首先是些科普知,理解的兄弟自行快速跳

   

          在托管内存管理中,”泄漏”意不同与传统 Native 用中的忘记显放(delete/delete[] 等)不同,当然于非托管源之(如句柄等)是需要在 Finalize (析构方法等同于 Finalize)方法中放的,在托管内存管理中”泄漏”例指的是,由于与 Root 象集中的象存在本的引用系,而 GC 线认为该对被使用,因而不能被放,尽管其不再会被使用。决大部分情况下,由于用(程序认为该对象不会存在了,而在再次使用,又在托管堆中再次建了该对例,可以想象这样的后果很重,随着建次数增加堆内存会爆。(托管堆中 G3 区爆,G2 区无法出空)。

   

          GC 判断一个象是否可以被放是通从被称 Root 象集中的根始(如 Main 函数的 args 形参、static 量及其象成等),遍出所有被其引用的象和子象。GC 过标记这些引用中的象,清除未标记上的象来完成内存放(标记、清理算法),当然清除也可能分(如移送 Finalize 列等)。由于标记、清理算法的中断时间等性能考,托管堆会分区(代),当前 CLR 是 3 代 – G1、G2、G3。伴随 Age(GC 一次 Age 加 1)增加,象会逐从 G1 移送到 G3 代中(制、整理算法),即 G1 是新生代,都是些短期象,G3 是老年象的永久居留地。需要明的是,实际上在当前版本的 .NET CLR 中有 2 个托管堆(SOH 和 LOH),其中一个叫大象托管堆(LOH),专门用来存放大于 84, 999 Bytes 的象。程序只能在 SOH G1 和 LOH 中分配象空,只有 CLR GC 线程可以在 SOH 的 G2、G3 中分配(移送)象。

   

          明白上面的基本道理,下面看看和托管例内存泄漏的例:

   

   

          上面中表示的意思是使用一段时间后,堆中象与 Root 象的引用系,其中色由浅到深表示了 Age 的因素。如果此,GC 线行,堆情况将如下所示:

   


          其中所有 Unreachable 的
例都将被 GC 所放,这样托管堆内存会被正确回收。但需要明的是,如果在 Reachable 的区域中(部分 GC 是不会放的),有一些被引用的象在以后不会再使用,而且用(程序)在下次使用时还建新的“泄漏”就生了。当类对建操作的业务被用复执行后,CLR 的 G3 代托管堆段会逐,服的死期也就不了。

   

          有了以上的知,可以说对内存泄漏的构化断、定位方法如下:

   

  1. 控托管堆使用量(程的内存占用量也可以),找到内存只不降的业务些代有内存泄漏的危程我一般会使用 LoadRunner 脚本来做,竟小尺寸象的泄漏需要较长时间才能生,靠手工操作不靠个一般不需要并
  2. 重新启动应用,托管堆清理无关对象;
  3. 行一次第1步发现的存在内存泄漏缺陷的业务
  4. 使用工具将托管堆出(dump)来,或托管堆做一次快照(snapshot)。在 dump/ snapshot 前要做一次全面 GC(full GC),尽量把可放干,排除干。此泄漏的象已不能 GC 掉了,会保存在托管堆中,都会被 dump/shot 出来;
  5. 复步骤 3。会再次建上次步骤 3)泄漏的象;
  6. 复步骤 4。此,泄漏的象是作本次 dump/shot 的新象存在的,相步骤 4 中泄漏的同类对象而言;
  7. 过对步骤 4 和 6 两次 dump/snapshot 果,下面就需要在茫茫象中找出泄漏的象/型来了。实际程是相的,需要了解设计,相背景知,了然的越详细,定位越快,果越准、完整。做两次 dump/snapshot 的目的在于,泄漏象将属于”新”象集范将有效小需检查象范。需要明的是,里的”新”指的是第二个 dump/snapshot 相于第一个 dump/snapshot 里存在的新象;
  8. 可能前步骤象范围还是比大,接下来可以从型角度排个检查级顺序:
  • 检查应用命名空型的象;
  • 检查框架所提供的型的象;检查经执动关闭/放方法的象。如 .NET 中的 IDisposable 接口的 Dispose 方法。因一旦用了这种方法,象本应该被 GC 所放的,它是不应该再存在的。于此类对象,存在的原因有可能:
    A.被短生命周期的
    象所引用,如局部量(包括形参量)等,造成无法 GC。但在,在再次行 dump/snapshot ,它应该 GC 放掉。
    B.
    用中设计了”池”,然被关闭,但它仍然会在池中存放,供下次使用再打。一般这种情况比,尤其在 .NET 中,放入池中的象很少会用其 Dispose 方法。方面 Java 也似。

          实际上面所构化方法只是表达个意思,真程会是一个逐定位、迭代的程,在理解其中意的前提下,灵活使用。通上面的一系列分析、断方法来定位到泄漏的象后,就找是它是被哪些象所引用,即 Root Path 中都有哪些象,通修改代来切断不存在的引用,就可以使泄漏行正常的 Unreachable 状,GC 线程也就会正确理它了。实际上,重点是在分析、断、定位,修方法是很容易找到的。

   

解决 ASP.NET 用内存泄漏问题 –  .Net Memory Profiler 工具

          首先要看下 .NET Memory Profiler 是什,就不翻了。我理解它实际就是个托管堆的 snapshot 工具,可以标记出非托管源。实时堆使用形的功能在没大用。


          .NET Memory Profiler is a tool for the .NET Common Language Runtime that allows the user to retrieve information about all instance allocations performed on the garbage collected heap (GC heap) and all instances that reside on the GC heap. The retrieved information is presented in real time, both numerically and graphically.

   

          再说说这个 ASP.NET 用,前两天事部的一个 ASP.NET 用出了内存泄漏的情况。象是,在一个查询业务场景中,发现查询几次之后 IIS 的 w3p 工作程会增几兆、十几兆、几十兆不等的内存(查询结果大小成正比),而且通 perfmon 控可以看到 w3p 的 CLR 及时执行了 GC,但托管堆使用量始只增不减,直到服宕掉(在 LoadRunner 测试时还有 w3p crash 的情况)。

          里就不详细说明使用 .NET Memory Profiler 工具使用细节了,相信能看到里的朋友,对这个工具的基本使用肯定不会成问题的。下面直接重点。

          按照上面提供的方法(内存泄漏的构化断、定位方法),首先我找到了在在问题业务,也能它。接下来将服重启, w3p 的托管堆初始干,排除无关对象。然后行一次有问题业务里我使用的 LoadRunner Vuser 回放测试脚本。接下来就要 .NET Memory Profiler 出 w3p 程托管堆做次 snapshot。个快照之前 .NET Memory Profiler 会自做一次 Full GC。再来做一次查询业务,然后再做snapshot。通过过两次比可以看到如下内容:

   

   

从中可以了解到:

   

  1. 的所有内容是其于 3# snapshot 与 2# snapshot 两次快照的果;
  2. .NET Memory Profiler 提供了 Types 所有照到的型,Show types 可以指定型的范,包括所有、”新”象(第二个 dump/snapshot 相于第一个 dump/snapshot 里存在的新象)等;Type details 型的例信息;Instance details 例的详细信息;Call stacks/Methods 方法;Native memory 非托管内存信息;
  3. 另外需要注意的是 Field Sets,包括 Standard、Dispose Info、Heap Utilization,用于过滤显示的 Types 果。比有用,能看到上面方法中提到的 Dispose 方法用后仍然在在的型有哪些,可以检查,下面会及到。

          接下来就需要有针对性的逐一排了,道先要考的是哪种类型的象最有可能生泄漏。上面的方法中提供了级别。我在里是这样的,因为对应用比了解,我知道在有问题业务页面中,有一个 static 型的成员变量,它是 XmlDataSource 型的象,由于 static 所以例不会被 GC,所以检查它所引用的有哪些例就很有意了。该类型的详细信息可

   

   

          唯一的 XmlDataSource 例是 #13, 483 号(全局号)象,通过详细信息可以看到如下被其引用的象集:

   

   

          通过对引用它的象一一检查发现,#13, 483 号的 XmlDataSource 例共引用了 6 个 XmlDataSourceView 象(之所以是 6 个,是因我做了 3 次 snapshot,次 LoadRunner 测试脚本中都行了 2 次查询操作),通的 Age 可以看到都经过了多次 GC,但都没有放掉。

   

   

          具体到 Root 象集的引用路径也很简单,只有一条,它都引用了#13, 483 号例的事件。通过这样的分析,我就能找到由于与 static XmlDataSource 象的引用系,造成了查询生成的 XmlDataSourceView象都无法被 GC 回收,因此形成了内存泄漏情况。解决法也很简单,可以通面中 XmlDataSource 象做非 static 成员变量即可。

   

          通上面象泄漏问题,我们每业务可以节约接近 11M 的托管内存。但是通之前的控来看,业务所增加的内存量要比大很多,而且通上面的问题也不能完全解决内存持的情况。可以定,该业务存在有内存泄漏的其它位置,需要继续分析、断。次我通 Dispose Info 着手,上面说过了,.NET 象在经过程序用 Dispose 方法后,就应该不再被使用并被 GC 所回收,如果在 snapshot 发现执行 Dispose 方法后 GC 多次仍未能回收的,就需要注了。下面就是 Dispose 后的型:

   

   

          果然,可以看到在我们业务页面及其 Master 象全部都是 Dispose,但未 GC 掉。通开类详细信息来看可以发现,他都被 SiteMap 所引用。

   

   

          我都知道 SiteMap 及 XmpSiteMapProvider 等 ASP.NET 2.0 中站点地件,它都是由 .NET Framework 提供和管理的,我是通 site 文件配置出来的,那造成个原因是什呢?通过调查了解到 SiteMap 象的 SiteMapResolve 事件是static的,而 SiteMap 也是全局性的,只要我们这订阅了 SiteMapResolve 事件,就都会引用到 SiteMap 上去。有趣的朋友可参 MSDN SiteMap::SiteMapResolve Event 的SiteMapResolve causing memory leaks 小。而且,从的内存使用量上看也比合理了,面由于包括了大量的控件及其状数据,因此都很大,象有 46M 多。

   

写在最后

          整个西我写得是相对较完整、详细和易践的,主要的原因是发现的一些人得托管堆的内存泄漏分析、断和定位是个能用来”吃“的活,非常不愿意和人分享。应该说这个事的确是很有经验含量的,这种经验化的西都愿意私藏,等必要的候拿来炫耀。但我想的是,我需要放,需要承,但凡是能文字化的西都是可以用来言的(文字的意)。在一个不提倡分享的境里要自己研究,要裹足不前,如果没有想法的根本不要注。


希望有用,如有想法
分享。安~

   

// 2009.03.06 01:01 添加 ////

   

          充个附件”lesson2.zip”,是 .NET Memory Profiler 内存泄漏分析、断的官方视频。使用的版本有点老了,而且例子用太简单,不其中的一些提示得考

.NET Memory Profiler 网站

   

// 2009.03.07 13:23 添加 ////

 

作者:lzy.je
:http://lzy.iteye.com
本文版
权归作者所有,只允以摘要和完整全文两形式转载,不允许对文字行裁剪。未作者同意必保留此段声明,且在文章面明位置出原文接,否保留追究法律任的利。