• 【转】Net资源泄露(内存泄露,GDI泄露,handle 泄露等)的终极解决方案


    摘要

      本文主要讨论了,什么是.Net内存泄露?如何确定是发生了内存泄露?如何预防内存泄露的发生?

    正文

    1.dot Net内存泄露简介

       可能很多.Net的用户(甚至包括一些dot Net开发者)对Net的内存泄露不是很了解,甚至会说.Net不存在内存泄露,因为“不是有GC机制吗?”----恩,是有这么回事,它可以让你在通常应用中不用考虑令人头疼的资源释放问题,但很遗憾的是这个机制不保证你开发的程序就不存在内存泄露。甚至可以说,dot Net中内存泄露是很常见的。这是因为:  一方面,GC机制本身的缺陷造成的;另一方面,Net中托管资源和非托管资源的处理是有差异的,托管资源的处理是由GC自动执行的(执行时机是不可预知的),而非托管资源 (占少部分,比如文件操作,网络连接等)必须显式地释放,否则就可能造成泄露。综合起来说的话,由于托管资源在Net中占大多数,通常不做显式的资源释放是可以的,不会造成明显的资源泄露,而非托管资源则不然,是发生问题的主战场,是最需要注意的地方。 另外,很多情况下,衰老测试主要关注的是有没有内存泄露的发生,而对其他泄露的重视次之。这是因为,内存跟其他资源是正相关的,也就是说没有内存泄露的发生,其他泄露的发生概率也较小,其根本原因在于几乎所有的资源最后都会在内存上有所反应。

    2..Net内存泄露的检测

    有没有内存泄露的发生?判断依据是那些?
      如果程序报“Out of memory”之类的错误,事实上也占据了很大部分的内存,应该说是典型的内存泄露,这种情况属于彻底的Bug,解决之道就是找到问题点,改正。但我的经验中,这种三下两下的就明显的泄露的情况较少,除非有人在很困的情况下编码,否则大多是隐性或渐进式地泄露,这种需经过较长时间的衰老测试才能发现,或者在特定条件下才出现,对这种情况要确定问题比较费劲,有一些工具(详见1.3)可以利用,但我总感觉效果一般,也可能是我不会使用吧,我想大型程序估计得无可奈何的用这个,详细的参见相关手册。
      需要强调的是,判断一个程序是不是出现了"memory leak",关键不是看它占用的内存有多大,而是放在一个足够长的时期(程序进入稳定运行状态后)内,看内存是不是还是一直往上涨,因此,刚开始的涨动或者前期的涨动不能做为泄露的充分证据。
      以上是些比较感性的说法,实际操作中是通过一些性能计数器来测定的。大多数时候,主要关注 Process 里的以下几个指标就能得出结论,如果这些量整体来看是持续上升的,基本可以判断是有泄露情况存在的。
      A.Handle Count
      B.Thread Count
      C.Private Bytes
      D.Virtual Bytes
      E.Working Set
      F.另外.NET CLR Memory下的Bytes in all heeps也是我比较关注的。
      通过观察,如果发现这些参数是在一个区间内震荡的,应该是没有大的问题,但如果是一个持续上涨的状态,那就得注意,很可能存在内存泄露。

    3.内存泄露诊断工具

        3.1perfmon.msc 的使用

           如何测定那些性能计数器呢,大多使用windows自带的perfmon.msc。

          在Run中输入perfmon.msc,运行,其他的自己摸索,不难。

       3.2 其他重要的性能计数器

          其他重要的计数器

       3.3其他检测工具

      我用过的工具中CLRProfilerdotTrace 还行,windeg也还行。不过坦白的说,准确定位比较费劲,最好还是按常规的该Dispose的加Dispose,也可以加 GC.Collect()。

    4.如何制造出健壮的程序

      4.1 Dispose()的使用

         如果使用的对象提供Dispose()方法,那么当你使用完毕或在必要的地方(比如Exception)调用该方法,特别是对非托管对象,一定要加以调 用,以达到防止泄露的目的。另外很多时候程序提供对Dispose()的扩展,比如Form,在这个扩展的Dispose方法中你可以把大对象的引用什么 的在退出前释放。

       对于DB连接,COM组件(比如OLE组件)等必须调用其提供的Dispose方法,没有的话最好自己写一个。

      4.2 using的使用

       using除了引用Dll的功用外,还可以限制对象的适用范围,当超出这个界限后对象自动释放,比如

      4.3 事件的卸载

       这个不是必须的,推荐这样做。之前注册了的事件,关闭画面时应该手动注销,有利于GC回收资源。

      4.4 API的调用

           一般的使用API了就意味着使用了非托管资源,需要根据情况手动释放所占资源,特别是在处理大对象时。

      4.5继承 IDisposable实现自己内存释放接口

        Net 如何继承IDisposable接口,实现自己的Dispose()函数

      4.6弱引用(WeakReference

         通常情况下,一个实例如果被其他实例引用了,那么他就不会被GC回收,而弱引用的意思是,如果一个实例没有被其他实例引用(真实引用),而仅仅是被弱引 用,那么他就会被GC回收。

      4.7析构函数(Finalize())

         使用了非托管资源的时候,可以自定义析构函数使得对象结束时释放所占资源;       

         对仅使用托管资源的对象,应尽可能使用它自身的Dispose方法,一般不推荐自定义析构函数。

    5.其他资源泄露

       GDI leak,handle leak。

    6. 几个特例

    1.对于使用了Bitmap对象的部分需要调用DestroyIcon来删除对象

     [DllImport("user32", EntryPoint = "DestroyIcon")]
     private static extern void DestroyIcon(IntPtr handle);


     using (System.IO.MemoryStream mstream = new System.IO.MemoryStream())
     {
         bmp.Save(mstream, System.Drawing.Imaging.ImageFormat.Png);
         intP = new Bitmap(mstream).GetHicon(); this.notifyIcon.Icon = Icon.FromHandle(intP);
         DestroyIcon(intP);
     }  

    2. 多线程中的GC         

                 //Force garbage collection.
                GC.Collect();
                //Wait for all finalizers to complete
                GC.WaitForPendingFinalizers();

    7.参考文献

      1.发 现并防止 托管代码中出现内存泄漏

      2..NET Memory Leak reader email: Are you really “leaking” .net memory

      3.How to detect and avoid memory and resources leaks in .NET applications

     4.其他参考资料

    8.扩展阅读

    1.实 用.net内存泄露(memory leak)解决方案

    2.Garbage Collection: Automatic Memory Management in the Microsoft .NET Framework By Jeffrey Richter  http://msdn.microsoft.com/en-us/magazine/bb985010.aspx

    3.OutOfMemoryException and Pinning

    https://blogs.msdn.com/yunjin/archive/2004/01/27/63642.aspx

    4.http://blogs.msdn.com/b/tess/

    5.http://msdn.microsoft.com/zh-cn/library/0xy59wtx%28v=VS.80%29.aspx

    后记

       其实本文更像是一篇如何开发出更健壮dot Net程序的指南。

    ---- 全文完-----

  • 相关阅读:
    线程间的通信 与 线程池
    线程同步
    静态代理模式
    多线程状态
    线程、进程、多线程
    Java面向对象之泛型
    ConstraintLayout 用法
    搞NDK开发
    Linux基础命令【记录】
    c# 的一些基本操作或属性
  • 原文地址:https://www.cnblogs.com/rainuu/p/2170137.html
Copyright © 2020-2023  润新知