.NET深入学习笔记(3):垃圾回收与内存管理


   今天抽空来讨论一下.Net的垃圾回收与内存管理机制,也算是完成上个《WCF分布式开发必备知识》系列后的一次休息吧。以前被别人面试的时候问过我GC工作原理的问题,我现在面试新人的时候偶尔也会问相关的问题。那么你是否也遇到这样的问题呢?比如你清楚.Net的垃圾回收机制吗?你能简述一下GC的工作原理吗?怎么样才能有效的管理内存呢?Using语句体内实例化的对象有什么作用?等等相关问题。下面我们就来详细讨论一下。相信你看完以后也可以面试别人。

     本节的组织如下,1..Net的类型和内存分配2.GC垃圾收集器的工作原理3.什么是非托管资源4.如何有效释放对象资源。总结.现在开始我们本节的学习。
    1..Net的类型和内存分配
     Net中的所有类型都是(直接或间接)从System.Object类型派生的。
    CTS中的类型被分成两大类——引用类型(reference type,又叫托管类型[managed type]),分配在内存堆上,值类型(value type)。值类型分配在堆栈上。如图
.NET深入学习笔记(3):垃圾回收与内存管理
    值类型在栈里,先进后出,值类型变量的生命有先后顺序,这个确保了值类型变量在推出作用域以前会释放资源。比引用类型更简单和高效。堆栈是从高地址往低地址分配内存。
     引用类型分配在托管堆(Managed Heap)上,声明一个变量在栈上保存,当使用new创建对象时,会把对象的地址存储在这个变量里。托管堆相反,从低地址往高地址分配内存,如图
.NET深入学习笔记(3):垃圾回收与内存管理
2.GC垃圾收集器的工作原理
      上图中,当dataSet使用过期以后,我们不显示销毁对象,堆上的对象还继续存在,等待GC的 回收。
垃圾收集器通过分代支持对象的年龄化是推荐的但不是必需的。一代在内存里是一个具有相对年龄的对象的单位。对象的
代号或年龄标识对象属于那个分代。在应用程序的生命周期里,越近创建的对象属于越新的代,并且比早创建的对象具有
较低的分代号。最近分代里的对象代号是0.
      在new对象时,要先搜索空闲链表,找到最适合内存块,分配,调整内存块链表,合并碎片。new操作几乎可以在O(1)的时间完成,把堆顶指针加1。工作原理是: 当托管堆上剩余空间不足,或者Generator 0 的空间已满的时候GC运行,开始回收内存。垃圾回收的开始,GC对堆内存的压缩调整,对象集中到顶部。GC在扫描垃圾的时候会占用一定的CPU时间片的,最初的GC算法真的是扫描整个堆,效率低。现在的GC把堆中的对象分成3代,最进入堆的是第0代(generation 0), 其次是generation 1, generation2. 第一次GC只扫描第0代。如果回收的空间足够当前使用就不必扫描其它generation的对象。所以,GC创建对象的效率比C++高效,不需要扫描全部堆空间。它通过扫描策略,再加上内存管理策略带来的性能提升,足以补偿GC所占用的CPU时间。
    3.什么是非托管资源
  常见的非托管资源就是包装操作系统资源的对象,例如文件,窗口或网络连接,对于这类资源虽然垃圾回收器可以跟踪封装非托管资源的对象的生存期,但它知道如何清理这些资源。好在.net Framework提供的Finalize()方法,它允许在垃圾回收器回收该类资源前,适当的清理非托管资源。这里列举几种常见的非托管资源:画笔、流对象、组件对象等等资源(Object,OdbcDataReader,OleDBDataReader,Pen,Regex,Socket,StreamWriter,ApplicationContext,Brush,
Component,ComponentDesigner,Container,Context,Cursor,FileStream,
Font,Icon,Image,Matrix,Timer,Tooltip)。(参考MSDN)
    4.如何有效释放非托管资源。
     GC无法管理非托管资源,那么如何释放非托管资源呢?.Net提供了两种方式:
(1)析构函数:垃圾收集器回收非托管对象的资源时,会调用对象的终结方法Finalize(),进行资源的清理工作,但是由于GC工作规则的限制,GC调用对象的Finalize方法,第一次不会释放资源,第二次调用之后才删除对象。
(2)继承IDisposable接口,实现Dispose()方法,IDisposable接口定义了一个模式(具有语言级的支持),为释放未托管的资源提供了确定的机制,并避免产生析构函数固有的与垃圾收集器相关的问题。
   为了更好的理解垃圾回收机制,我特地写了部分代码,里面添加了详细的注释。定义单个类FrankClassWithDispose(继承接口IDisposableFrankClassNoFinalize(没终结器)FrankClassWithDestructor(定义了析构函数)。
具体代码如下:
.NET深入学习笔记(3):垃圾回收与内存管理1using System; 
.NET深入学习笔记(3):垃圾回收与内存管理 2using System.Collections.Generic; 
.NET深入学习笔记(3):垃圾回收与内存管理 3using System.Text; 
.NET深入学习笔记(3):垃圾回收与内存管理 4using System.Data; 
.NET深入学习笔记(3):垃圾回收与内存管理 5using System.Data.Odbc; 
.NET深入学习笔记(3):垃圾回收与内存管理 6using System.Drawing; 
.NET深入学习笔记(3):垃圾回收与内存管理 7//Coded By Frank Xu Lei 18/2/2009 
.NET深入学习笔记(3):垃圾回收与内存管理 8//Study the .NET Memory Management 
.NET深入学习笔记(3):垃圾回收与内存管理 9//Garbage Collector 垃圾收集器。可以根据策略在需要的时候回收托管资源, 
.NET深入学习笔记(3):垃圾回收与内存管理10//但是GC不知道如何管理非托管资源。如网络连接、数据库连接、画笔、组件等 
.NET深入学习笔记(3):垃圾回收与内存管理11//两个机制来解决非托管资源的释放问题。析构函数、IDispose接口 
.NET深入学习笔记(3):垃圾回收与内存管理12//COM引用计数 
.NET深入学习笔记(3):垃圾回收与内存管理13//C++手动管理,New Delete 
.NET深入学习笔记(3):垃圾回收与内存管理14//VB自动管理 
.NET深入学习笔记(3):垃圾回收与内存管理15namespace MemoryManagement 
.NET深入学习笔记(3):垃圾回收与内存管理16{ 
.NET深入学习笔记(3):垃圾回收与内存管理17        //继承接口IDisposable,实现Dispose方法,可以释放FrankClassDispose的实例资源 
.NET深入学习笔记(3):垃圾回收与内存管理18        public class FrankClassWithDispose : IDisposable 
.NET深入学习笔记(3):垃圾回收与内存管理19        { 
.NET深入学习笔记(3):垃圾回收与内存管理20                private OdbcConnection _odbcConnection = null
.NET深入学习笔记(3):垃圾回收与内存管理21                 
.NET深入学习笔记(3):垃圾回收与内存管理22                //构造函数 
.NET深入学习笔记(3):垃圾回收与内存管理23                public FrankClassWithDispose() 
.NET深入学习笔记(3):垃圾回收与内存管理24                { 
.NET深入学习笔记(3):垃圾回收与内存管理25                        if (_odbcConnection == null
.NET深入学习笔记(3):垃圾回收与内存管理26                                _odbcConnection = new OdbcConnection(); 
.NET深入学习笔记(3):垃圾回收与内存管理27                        Console.WriteLine("FrankClassWithDispose has been created "); 
.NET深入学习笔记(3):垃圾回收与内存管理28                } 
.NET深入学习笔记(3):垃圾回收与内存管理29                //测试方法 
.NET深入学习笔记(3):垃圾回收与内存管理30                public void DoSomething() 
.NET深入学习笔记(3):垃圾回收与内存管理31                { 
.NET深入学习笔记(3):垃圾回收与内存管理32 
.NET深入学习笔记(3):垃圾回收与内存管理33                        /**/////code here to do something 
.NET深入学习笔记(3):垃圾回收与内存管理34                        return ; 
.NET深入学习笔记(3):垃圾回收与内存管理35                } 
.NET深入学习笔记(3):垃圾回收与内存管理36                //实现Dispose,释放本类使用的资源 
.NET深入学习笔记(3):垃圾回收与内存管理37                public void Dispose() 
.NET深入学习笔记(3):垃圾回收与内存管理38                { 
.NET深入学习笔记(3):垃圾回收与内存管理39                        if (_odbcConnection != null
.NET深入学习笔记(3):垃圾回收与内存管理40                                _odbcConnection.Dispose(); 
.NET深入学习笔记(3):垃圾回收与内存管理41                        Console.WriteLine("FrankClassWithDispose has been disposed"); 
.NET深入学习笔记(3):垃圾回收与内存管理42                } 
.NET深入学习笔记(3):垃圾回收与内存管理43        } 
.NET深入学习笔记(3):垃圾回收与内存管理44        //没有实现Finalize,等着GC回收FrankClassFinalize的实例资源,GC运行时候直接回收 
.NET深入学习笔记(3):垃圾回收与内存管理45        public class FrankClassNoFinalize 
.NET深入学习笔记(3):垃圾回收与内存管理46        { 
.NET深入学习笔记(3):垃圾回收与内存管理47                private OdbcConnection _odbcConnection = null
.NET深入学习笔记(3):垃圾回收与内存管理48                //构造函数 
.NET深入学习笔记(3):垃圾回收与内存管理49                public FrankClassNoFinalize() 
.NET深入学习笔记(3):垃圾回收与内存管理50                { 
.NET深入学习笔记(3):垃圾回收与内存管理51                        if (_odbcConnection == null
.NET深入学习笔记(3):垃圾回收与内存管理52                                _odbcConnection = new OdbcConnection(); 
.NET深入学习笔记(3):垃圾回收与内存管理53                        Console.WriteLine("FrankClassNoFinalize    has been created"); 
.NET深入学习笔记(3):垃圾回收与内存管理54                } 
.NET深入学习笔记(3):垃圾回收与内存管理55                //测试方法 
.NET深入学习笔记(3):垃圾回收与内存管理56                public void DoSomething() 
.NET深入学习笔记(3):垃圾回收与内存管理57                { 
.NET深入学习笔记(3):垃圾回收与内存管理58 
.NET深入学习笔记(3):垃圾回收与内存管理59                        //GC.Collect(); 
.NET深入学习笔记(3):垃圾回收与内存管理60                        /**/////code here to do something 
.NET深入学习笔记(3):垃圾回收与内存管理61                        return ; 
.NET深入学习笔记(3):垃圾回收与内存管理62                } 
.NET深入学习笔记(3):垃圾回收与内存管理63        } 
.NET深入学习笔记(3):垃圾回收与内存管理64        //实现析构函数,编译为Finalize方法,调用对象的析构函数 
.NET深入学习笔记(3):垃圾回收与内存管理65        //GC运行时,两次调用,第一次没释放资源,第二次才释放 
.NET深入学习笔记(3):垃圾回收与内存管理66        //FrankClassDestructor的实例资源 
.NET深入学习笔记(3):垃圾回收与内存管理67        //CLR使用独立的线程来执行对象的Finalize方法,频繁调用会使性能下降 
.NET深入学习笔记(3):垃圾回收与内存管理68        public class FrankClassWithDestructor 
.NET深入学习笔记(3):垃圾回收与内存管理69        { 
.NET深入学习笔记(3):垃圾回收与内存管理70                private OdbcConnection _odbcConnection = null
.NET深入学习笔记(3):垃圾回收与内存管理71                //构造函数 
.NET深入学习笔记(3):垃圾回收与内存管理72                public FrankClassWithDestructor() 
.NET深入学习笔记(3):垃圾回收与内存管理73                { 
.NET深入学习笔记(3):垃圾回收与内存管理74                        if (_odbcConnection == null
.NET深入学习笔记(3):垃圾回收与内存管理75                                _odbcConnection = new OdbcConnection(); 
.NET深入学习笔记(3):垃圾回收与内存管理76                        Console.WriteLine("FrankClassWithDestructor    has been created"); 
.NET深入学习笔记(3):垃圾回收与内存管理77                } 
.NET深入学习笔记(3):垃圾回收与内存管理78                //测试方法 
.NET深入学习笔记(3):垃圾回收与内存管理79                public void DoSomething() 
.NET深入学习笔记(3):垃圾回收与内存管理80                { 
.NET深入学习笔记(3):垃圾回收与内存管理81                        /**/////code here to do something 
.NET深入学习笔记(3):垃圾回收与内存管理82 
.NET深入学习笔记(3):垃圾回收与内存管理83                        return ; 
.NET深入学习笔记(3):垃圾回收与内存管理84                } 
.NET深入学习笔记(3):垃圾回收与内存管理85                //析构函数,释放未托管资源 
.NET深入学习笔记(3):垃圾回收与内存管理86                ~FrankClassWithDestructor() 
.NET深入学习笔记(3):垃圾回收与内存管理87                { 
.NET深入学习笔记(3):垃圾回收与内存管理88                        if (_odbcConnection != null
.NET深入学习笔记(3):垃圾回收与内存管理89                                _odbcConnection.Dispose(); 
.NET深入学习笔记(3):垃圾回收与内存管理90                        Console.WriteLine("FrankClassWithDestructor    has been disposed"); 
.NET深入学习笔记(3):垃圾回收与内存管理91                } 
.NET深入学习笔记(3):垃圾回收与内存管理92        } 
.NET深入学习笔记(3):垃圾回收与内存管理93} 
.NET深入学习笔记(3):垃圾回收与内存管理94 
 
其中使用了非托管的对象OdbcConnection的实例。建立的客户端进行了简单的测试。客户端代码如下:
.NET深入学习笔记(3):垃圾回收与内存管理1using System; 
.NET深入学习笔记(3):垃圾回收与内存管理 2using System.Collections.Generic; 
.NET深入学习笔记(3):垃圾回收与内存管理 3using System.Text; 
.NET深入学习笔记(3):垃圾回收与内存管理 4using System.Data; 
.NET深入学习笔记(3):垃圾回收与内存管理 5using MemoryManagement; 
.NET深入学习笔记(3):垃圾回收与内存管理 6//Coded By Frank Xu Lei 18/2/2009 
.NET深入学习笔记(3):垃圾回收与内存管理 7//Study the .NET Memory Management 
.NET深入学习笔记(3):垃圾回收与内存管理 8//Test The Unmanaged Objects Reclaimed. 
.NET深入学习笔记(3):垃圾回收与内存管理 9//针对非托管代码的测试,比较 
.NET深入学习笔记(3):垃圾回收与内存管理10//托管代码,GC可以更具策略自己回收,也可以实现IDisposable,调用Dispose()方法,主动释放。 
.NET深入学习笔记(3):垃圾回收与内存管理11namespace MemoryManagementClient 
.NET深入学习笔记(3):垃圾回收与内存管理12{ 
.NET深入学习笔记(3):垃圾回收与内存管理13        class Program 
.NET深入学习笔记(3):垃圾回收与内存管理14        { 
.NET深入学习笔记(3):垃圾回收与内存管理15                static void Main(string[] args) 
.NET深入学习笔记(3):垃圾回收与内存管理16                { 
.NET深入学习笔记(3):垃圾回收与内存管理17 
.NET深入学习笔记(3):垃圾回收与内存管理18                        /**//////////////////////////////////////////(1)//////////////////////////////////////////// 
.NET深入学习笔记(3):垃圾回收与内存管理19                        //调用Dispose()方法,主动释放。资源,灵活 
.NET深入学习笔记(3):垃圾回收与内存管理20                        FrankClassWithDispose _frankClassWithDispose = null
.NET深入学习笔记(3):垃圾回收与内存管理21                        try 
.NET深入学习笔记(3):垃圾回收与内存管理22                        { 
.NET深入学习笔记(3):垃圾回收与内存管理23                                _frankClassWithDispose = new FrankClassWithDispose(); 
.NET深入学习笔记(3):垃圾回收与内存管理24                                _frankClassWithDispose.DoSomething(); 
.NET深入学习笔记(3):垃圾回收与内存管理25                                 
.NET深入学习笔记(3):垃圾回收与内存管理26                        } 
.NET深入学习笔记(3):垃圾回收与内存管理27                        finally 
.NET深入学习笔记(3):垃圾回收与内存管理28                        { 
.NET深入学习笔记(3):垃圾回收与内存管理29                                if (_frankClassWithDispose!=null
.NET深入学习笔记(3):垃圾回收与内存管理30                                _frankClassWithDispose.Dispose(); 
.NET深入学习笔记(3):垃圾回收与内存管理31                                //Console.WriteLine("FrankClassWithDispose实例已经被释放"); 
.NET深入学习笔记(3):垃圾回收与内存管理32                        } 
.NET深入学习笔记(3):垃圾回收与内存管理33                                 
.NET深入学习笔记(3):垃圾回收与内存管理34                        /**//////////////////////////////////////////(2)////////////////////////////////////////////// 
.NET深入学习笔记(3):垃圾回收与内存管理35                        //可以使用Using语句创建非托管对象,方法执行结束前,会调用 
.NET深入学习笔记(3):垃圾回收与内存管理36                        using (FrankClassWithDispose _frankClassWithDispose2 = new FrankClassWithDispose()) 
.NET深入学习笔记(3):垃圾回收与内存管理37                        { 
.NET深入学习笔记(3):垃圾回收与内存管理38                                //_frankClassWithDispose2.DoSomething(); 
.NET深入学习笔记(3):垃圾回收与内存管理39                        } 
.NET深入学习笔记(3):垃圾回收与内存管理40 
.NET深入学习笔记(3):垃圾回收与内存管理41                        /**//////////////////////////////////////////(3)//////////////////////////////////////////// 
.NET深入学习笔记(3):垃圾回收与内存管理42                        //垃圾收集器运行的时候,一次就释放资源 
.NET深入学习笔记(3):垃圾回收与内存管理43                        FrankClassNoFinalize _frankClassNoFinalize = new FrankClassNoFinalize(); 
.NET深入学习笔记(3):垃圾回收与内存管理44                        _frankClassNoFinalize.DoSomething(); 
.NET深入学习笔记(3):垃圾回收与内存管理45                            
.NET深入学习笔记(3):垃圾回收与内存管理46                        /**///////////////////////////////////////////(4)////////////////////////////////////////////// 
.NET深入学习笔记(3):垃圾回收与内存管理47                        //垃圾收集器运行的时候,两次才能够释放资源 
.NET深入学习笔记(3):垃圾回收与内存管理48                        FrankClassWithDestructor _frankClassWithDestructor = new FrankClassWithDestructor(); 
.NET深入学习笔记(3):垃圾回收与内存管理49                        _frankClassWithDestructor.DoSomething(); 
.NET深入学习笔记(3):垃圾回收与内存管理50                        /**////////////////////////////////////////////(5)///////////////////////////////////////////// 
.NET深入学习笔记(3):垃圾回收与内存管理51                        //不能使用Using语句来创建对象,因为其没实现IDispose接口 
.NET深入学习笔记(3):垃圾回收与内存管理52                        //using (FrankClassWithDestructor _frankClassWithDestructor2 = new FrankClassWithDestructor()) 
.NET深入学习笔记(3):垃圾回收与内存管理53                        //{ 
.NET深入学习笔记(3):垃圾回收与内存管理54                        //        _frankClassWithDestructor2.DoSomething(); 
.NET深入学习笔记(3):垃圾回收与内存管理55                        //} 
.NET深入学习笔记(3):垃圾回收与内存管理56 
.NET深入学习笔记(3):垃圾回收与内存管理57                        /**/////////////////////////////////////////////////////////////////////////////////////// 
.NET深入学习笔记(3):垃圾回收与内存管理58                        //For Debug 
.NET深入学习笔记(3):垃圾回收与内存管理59                        Console.WriteLine("Press any key to continue"); 
.NET深入学习笔记(3):垃圾回收与内存管理60                        Console.ReadLine(); 
.NET深入学习笔记(3):垃圾回收与内存管理61 
.NET深入学习笔记(3):垃圾回收与内存管理62                 
.NET深入学习笔记(3):垃圾回收与内存管理63                } 
.NET深入学习笔记(3):垃圾回收与内存管理64        } 
.NET深入学习笔记(3):垃圾回收与内存管理65} 
.NET深入学习笔记(3):垃圾回收与内存管理66 
有些时候资源必须在特定时间释放,类可以实现执行资源管理和清除任务方法IDisposable.Dispose的接口IDisposable。
如果调用者需要调用Dispose方法清理对象,类作为契约的一部分必须实现Dispose方法。垃圾收集器默认情况下不会调用
Dispose方法;然而,实现Dispose方法可以调用GC里的方法去规范垃圾收器的终结行为。
值得一提的是:调用Dispose()方法,主动释放资源,灵活,可以使用Using语句创建非托管对象,方法执行结束前,会调用
Dispose()方法释放资源,
这两端代码的效果是一样的,可以查看编译后IL。
.NET深入学习笔记(3):垃圾回收与内存管理1.try 
.NET深入学习笔记(3):垃圾回收与内存管理 2    { 
.NET深入学习笔记(3):垃圾回收与内存管理 3        IL_0003:    nop 
.NET深入学习笔记(3):垃圾回收与内存管理 4        IL_0004:    newobj         instance void [MemoryManagement]MemoryManagement.FrankClassWithDispose::.ctor() 
.NET深入学习笔记(3):垃圾回收与内存管理 5        IL_0009:    stloc.0 
.NET深入学习笔记(3):垃圾回收与内存管理 6        IL_000a:    ldloc.0 
.NET深入学习笔记(3):垃圾回收与内存管理 7        IL_000b:    callvirt     instance void [MemoryManagement]MemoryManagement.FrankClassWithDispose::DoSomething() 
.NET深入学习笔记(3):垃圾回收与内存管理 8        IL_0010:    nop 
.NET深入学习笔记(3):垃圾回收与内存管理 9        IL_0011:    nop 
.NET深入学习笔记(3):垃圾回收与内存管理10        IL_0012:    leave.s        IL_0028 
.NET深入学习笔记(3):垃圾回收与内存管理11    }    // end .try 
.NET深入学习笔记(3):垃圾回收与内存管理12    finally 
.NET深入学习笔记(3):垃圾回收与内存管理13    { 
.NET深入学习笔记(3):垃圾回收与内存管理14        IL_0014:    nop 
.NET深入学习笔记(3):垃圾回收与内存管理15        IL_0015:    ldloc.0 
.NET深入学习笔记(3):垃圾回收与内存管理16        IL_0016:    ldnull 
.NET深入学习笔记(3):垃圾回收与内存管理17        IL_0017:    ceq 
.NET深入学习笔记(3):垃圾回收与内存管理18        IL_0019:    stloc.s        CS$4$0000 
.NET深入学习笔记(3):垃圾回收与内存管理19        IL_001b:    ldloc.s        CS$4$0000 
.NET深入学习笔记(3):垃圾回收与内存管理20        IL_001d:    brtrue.s     IL_0026 
.NET深入学习笔记(3):垃圾回收与内存管理21        IL_001f:    ldloc.0 
.NET深入学习笔记(3):垃圾回收与内存管理22        IL_0020:    callvirt     instance void [MemoryManagement]MemoryManagement.FrankClassWithDispose::Dispose() 
.NET深入学习笔记(3):垃圾回收与内存管理23        IL_0025:    nop 
.NET深入学习笔记(3):垃圾回收与内存管理24        IL_0026:    nop 
.NET深入学习笔记(3):垃圾回收与内存管理25        IL_0027:    endfinally 
.NET深入学习笔记(3):垃圾回收与内存管理26    }    // end handler 
.NET深入学习笔记(3):垃圾回收与内存管理27    IL_0028:    nop 
.NET深入学习笔记(3):垃圾回收与内存管理28    IL_0029:    newobj         instance void [MemoryManagement]MemoryManagement.FrankClassWithDispose::.ctor() 
.NET深入学习笔记(3):垃圾回收与内存管理29    IL_002e:    stloc.1 
.NET深入学习笔记(3):垃圾回收与内存管理30    .try 
.NET深入学习笔记(3):垃圾回收与内存管理31    { 
.NET深入学习笔记(3):垃圾回收与内存管理32        IL_002f:    nop 
.NET深入学习笔记(3):垃圾回收与内存管理33        IL_0030:    nop 
.NET深入学习笔记(3):垃圾回收与内存管理34        IL_0031:    leave.s        IL_0045 
.NET深入学习笔记(3):垃圾回收与内存管理35    }    // end .try 
.NET深入学习笔记(3):垃圾回收与内存管理36    finally 
.NET深入学习笔记(3):垃圾回收与内存管理37    { 
.NET深入学习笔记(3):垃圾回收与内存管理38        IL_0033:    ldloc.1 
.NET深入学习笔记(3):垃圾回收与内存管理39        IL_0034:    ldnull 
.NET深入学习笔记(3):垃圾回收与内存管理40        IL_0035:    ceq 
.NET深入学习笔记(3):垃圾回收与内存管理41        IL_0037:    stloc.s        CS$4$0000 
.NET深入学习笔记(3):垃圾回收与内存管理42        IL_0039:    ldloc.s        CS$4$0000 
.NET深入学习笔记(3):垃圾回收与内存管理43        IL_003b:    brtrue.s     IL_0044 
.NET深入学习笔记(3):垃圾回收与内存管理44        IL_003d:    ldloc.1 
.NET深入学习笔记(3):垃圾回收与内存管理45        IL_003e:    callvirt     instance void [mscorlib]System.IDisposable::Dispose() 
.NET深入学习笔记(3):垃圾回收与内存管理46        IL_0043:    nop 
.NET深入学习笔记(3):垃圾回收与内存管理47        IL_0044:    endfinally 
.NET深入学习笔记(3):垃圾回收与内存管理48    }    // end handler 
.NET深入学习笔记(3):垃圾回收与内存管理49
Using 语句有同样的效果,来实现非托管对象资源的释放。这点在面试中也会经常遇到,Using关键字的用法有哪几种等等类似的问题。基本理想的答案都是除了引用命名空间,和命名空间设置别名外,就是这个用法实现如try finally块一样作用的对非托管对象资源的回收。只是一种简便的写法。
     当你用Dispose方法释放未托管对象的时候,应该调用GC.SuppressFinalize。如果对象正在终结队列(finalization queue),GC.SuppressFinalize会阻止GC调用Finalize方法。因为Finalize方法的调用会牺牲部分性能。如果你的Dispose方法已经对委托管资源作了清理,就没必要让GC再调用对象的Finalize方法(MSDN)。附上MSDN的代码,大家可以参考.
.NET深入学习笔记(3):垃圾回收与内存管理public class BaseResource: IDisposable 
.NET深入学习笔记(3):垃圾回收与内存管理
.NET深入学习笔记(3):垃圾回收与内存管理     // 指向外部非托管资源 
.NET深入学习笔记(3):垃圾回收与内存管理     private IntPtr handle; 
.NET深入学习笔记(3):垃圾回收与内存管理     // 此类使用的其它托管资源. 
.NET深入学习笔记(3):垃圾回收与内存管理     private Component Components; 
.NET深入学习笔记(3):垃圾回收与内存管理     // 跟踪是否调用.Dispose方法,标识位,控制垃圾收集器的行为 
.NET深入学习笔记(3):垃圾回收与内存管理     private bool disposed = false
.NET深入学习笔记(3):垃圾回收与内存管理 
.NET深入学习笔记(3):垃圾回收与内存管理     // 构造函数 
.NET深入学习笔记(3):垃圾回收与内存管理     public BaseResource() 
.NET深入学习笔记(3):垃圾回收与内存管理     { 
.NET深入学习笔记(3):垃圾回收与内存管理            // Insert appropriate constructor code here. 
.NET深入学习笔记(3):垃圾回收与内存管理     } 
.NET深入学习笔记(3):垃圾回收与内存管理 
.NET深入学习笔记(3):垃圾回收与内存管理     // 实现接口IDisposable. 
.NET深入学习笔记(3):垃圾回收与内存管理     // 不能声明为虚方法virtual. 
.NET深入学习笔记(3):垃圾回收与内存管理     // 子类不能重写这个方法. 
.NET深入学习笔记(3):垃圾回收与内存管理     public void Dispose() 
.NET深入学习笔记(3):垃圾回收与内存管理     { 
.NET深入学习笔记(3):垃圾回收与内存管理            Dispose(true); 
.NET深入学习笔记(3):垃圾回收与内存管理            // 离开终结队列Finalization queue    
.NET深入学习笔记(3):垃圾回收与内存管理            // 设置对象的阻止终结器代码 
.NET深入学习笔记(3):垃圾回收与内存管理            //    
.NET深入学习笔记(3):垃圾回收与内存管理            GC.SuppressFinalize(this); 
.NET深入学习笔记(3):垃圾回收与内存管理     } 
.NET深入学习笔记(3):垃圾回收与内存管理 
.NET深入学习笔记(3):垃圾回收与内存管理     // Dispose(bool disposing) 执行分两种不同的情况. 
.NET深入学习笔记(3):垃圾回收与内存管理     // 如果disposing 等于 true, 方法已经被调用 
.NET深入学习笔记(3):垃圾回收与内存管理     // 或者间接被用户代码调用. 托管和非托管的代码都能被释放 
.NET深入学习笔记(3):垃圾回收与内存管理     // 如果disposing 等于false, 方法已经被终结器 finalizer 从内部调用过, 
.NET深入学习笔记(3):垃圾回收与内存管理     //你就不能在引用其他对象,只有非托管资源可以被释放。 
.NET深入学习笔记(3):垃圾回收与内存管理     protected virtual void Dispose(bool disposing) 
.NET深入学习笔记(3):垃圾回收与内存管理     { 
.NET深入学习笔记(3):垃圾回收与内存管理            // 检查Dispose 是否被调用过. 
.NET深入学习笔记(3):垃圾回收与内存管理            if(!this.disposed) 
.NET深入学习笔记(3):垃圾回收与内存管理            { 
.NET深入学习笔记(3):垃圾回收与内存管理                 // 如果等于true, 释放所有托管和非托管资源    
.NET深入学习笔记(3):垃圾回收与内存管理                 if(disposing) 
.NET深入学习笔记(3):垃圾回收与内存管理                 { 
.NET深入学习笔记(3):垃圾回收与内存管理                        // 释放托管资源. 
.NET深入学习笔记(3):垃圾回收与内存管理                        Components.Dispose(); 
.NET深入学习笔记(3):垃圾回收与内存管理                 } 
.NET深入学习笔记(3):垃圾回收与内存管理                 // 释放非托管资源,如果disposing为 false,    
.NET深入学习笔记(3):垃圾回收与内存管理                 // 只会执行下面的代码. 
.NET深入学习笔记(3):垃圾回收与内存管理                 CloseHandle(handle); 
.NET深入学习笔记(3):垃圾回收与内存管理                 handle = IntPtr.Zero; 
.NET深入学习笔记(3):垃圾回收与内存管理                 // 注意这里是非线程安全的. 
.NET深入学习笔记(3):垃圾回收与内存管理                 // 在托管资源释放以后可以启动其它线程销毁对象, 
.NET深入学习笔记(3):垃圾回收与内存管理                 // 但是在disposed标记设置为true前 
.NET深入学习笔记(3):垃圾回收与内存管理                 // 如果线程安全是必须的,客户端必须实现。 
.NET深入学习笔记(3):垃圾回收与内存管理 
.NET深入学习笔记(3):垃圾回收与内存管理            } 
.NET深入学习笔记(3):垃圾回收与内存管理            disposed = true;                    
.NET深入学习笔记(3):垃圾回收与内存管理     } 
.NET深入学习笔记(3):垃圾回收与内存管理                // 使用interop 调用方法    
.NET深入学习笔记(3):垃圾回收与内存管理                // 清除非托管资源. 
.NET深入学习笔记(3):垃圾回收与内存管理                [System.Runtime.InteropServices.DllImport("Kernel32")] 
.NET深入学习笔记(3):垃圾回收与内存管理                private extern static Boolean CloseHandle(IntPtr handle); 
.NET深入学习笔记(3):垃圾回收与内存管理 
.NET深入学习笔记(3):垃圾回收与内存管理     // 使用C# 析构函数来实现终结器代码 
.NET深入学习笔记(3):垃圾回收与内存管理     // 这个只在Dispose方法没被调用的前提下,才能调用执行。 
.NET深入学习笔记(3):垃圾回收与内存管理     // 如果你给基类终结的机会. 
.NET深入学习笔记(3):垃圾回收与内存管理     // 不要给子类提供析构函数. 
.NET深入学习笔记(3):垃圾回收与内存管理     ~BaseResource()             
.NET深入学习笔记(3):垃圾回收与内存管理     { 
.NET深入学习笔记(3):垃圾回收与内存管理            // 不要重复创建清理的代码. 
.NET深入学习笔记(3):垃圾回收与内存管理            // 基于可靠性和可维护性考虑,调用Dispose(false) 是最佳的方式 
.NET深入学习笔记(3):垃圾回收与内存管理            Dispose(false); 
.NET深入学习笔记(3):垃圾回收与内存管理     } 
.NET深入学习笔记(3):垃圾回收与内存管理 
.NET深入学习笔记(3):垃圾回收与内存管理     // 允许你多次调用Dispose方法, 
.NET深入学习笔记(3):垃圾回收与内存管理     // 但是会抛出异常如果对象已经释放。 
.NET深入学习笔记(3):垃圾回收与内存管理     // 不论你什么时间处理对象都会核查对象的是否释放,    
.NET深入学习笔记(3):垃圾回收与内存管理     // check to see if it has been disposed. 
.NET深入学习笔记(3):垃圾回收与内存管理     public void DoSomething() 
.NET深入学习笔记(3):垃圾回收与内存管理     { 
.NET深入学习笔记(3):垃圾回收与内存管理            if(this.disposed) 
.NET深入学习笔记(3):垃圾回收与内存管理            { 
.NET深入学习笔记(3):垃圾回收与内存管理                 throw new ObjectDisposedException(); 
.NET深入学习笔记(3):垃圾回收与内存管理            } 
.NET深入学习笔记(3):垃圾回收与内存管理     } 
.NET深入学习笔记(3):垃圾回收与内存管理     对于需要调用Close方法比Dispose方法更加自然的类型,可以在    基类增加一个Close方法。 
.NET深入学习笔记(3):垃圾回收与内存管理    Close方法无参调用执行恰当清理工作的Dispose方法。 
.NET深入学习笔记(3):垃圾回收与内存管理    下面的例子演示了Close方法。 
.NET深入学习笔记(3):垃圾回收与内存管理     // 不要设置方法为virtual. 
.NET深入学习笔记(3):垃圾回收与内存管理     // 继承类不允许重写这个方法 
.NET深入学习笔记(3):垃圾回收与内存管理     public void Close() 
.NET深入学习笔记(3):垃圾回收与内存管理     { 
.NET深入学习笔记(3):垃圾回收与内存管理     // 无参数调用Dispose参数. 
.NET深入学习笔记(3):垃圾回收与内存管理             Dispose(); 
.NET深入学习笔记(3):垃圾回收与内存管理     } 
.NET深入学习笔记(3):垃圾回收与内存管理 
.NET深入学习笔记(3):垃圾回收与内存管理     public static void Main() 
.NET深入学习笔记(3):垃圾回收与内存管理     { 
.NET深入学习笔记(3):垃圾回收与内存管理                // Insert code here to create 
.NET深入学习笔记(3):垃圾回收与内存管理                // and use a BaseResource object. 
.NET深入学习笔记(3):垃圾回收与内存管理     } 
.NET深入学习笔记(3):垃圾回收与内存管理}
总结:看了本文以后,不知对你是否有所帮助,如果你理解了.net垃圾回收的机制和GC的工作原理,以及包含如何管理非托管资源,你就会成为一个内存管理的高手。如果面试官问道这个问题,你就可以详细阐述你对这类问题的理解和看法。希望这篇文章能对你的工作和学习带来帮助~



 本文转自 frankxulei 51CTO博客,原文链接:http://blog.51cto.com/frankxulei/318541,如需转载请自行联系原作者


上一篇:MySQL中left join相关问题整理


下一篇:微信打开网站被提示已停止访问该网页该如何解决