代码改变世界

.Net Comapct Framework的垃圾回收

2008-10-14 18:03  cppguy  阅读(717)  评论(2编辑  收藏  举报

1:托管资源

     什么是代龄:垃圾收集器将托管堆中的对象分为三代,分别为0,1,2.在CLR初始化时,会选择为三代设置不同的阙值容量,一般分配为:第0代大约为256KB,第一代2MB,第二代10MB。显然,容量越大效率越低,而GC收集器会自动调节其阙值容量来提升执行效率,第0代对象的回收效率肯定是最高的。

在CLR初始化后,首先被添加到托管堆中的对象都被定为第0代,当垃圾回收执行时,未被回收的对象代龄将提升一级,变成第1代对象,而后新建的对象为第0代对象。也就是说,代龄越小,对象越新,通常情况下其生命周期也最短,因此垃圾回收其总是首先收集第0代的不可达对象内存。

随着对象的不断创建,垃圾收集再次启动时,则只会检查0代对象,并回收0代对象,而1代对象由于未达到预定的1代容量阙值,则不会进行垃圾回收操作,从而有效的提高了垃圾收集效率,这就是代龄机制在垃圾回收中的性能优化作用。那么垃圾收集器在什么情况下,才执行对第一代对象的收集?答案是仅当第0代对象释放的内存不足以创建新的对象,同时1代对象的体积也超出了容量阙值时,垃圾收集器将同时对0代和1代对象进行垃圾回收。回收之后,未被回收的1代对象升级为2代对象。而新建的对象还是0代对象。垃圾收集正是对上述过程的不断重复,利用分代机制提高执行效率。

     通过GC.Collect方法可以指定对从第0代到指定代的对象进行回收。而.NET Comapct Framework只支持了GC.Collect(),强制对所有代进行即时垃圾回收,不能指定代龄。

  那什么样的对象才被GC认为是垃圾呢?

  简单的说,一个对象成为垃圾就表示该对象不被任何其他对象所引用了,因此,GC采用一定的算法在托管堆中遍历所有对象,最终形成一个可达对象图,而不可达的对象将成为被释放的垃圾对象等待收集

如何回收呢?

每个应用程序有一组根指针,,指向托管堆中的存储位置,由JIT编译器和CLR运行时维护根指针列表,主要包括全局变量,静态变量,局部变量和寄存器指针等。当垃圾收集器启动时,它假设所有对象都是可回收的垃圾,并开始遍历所有的根,将根引用的对象标记为可达对象添加到可达对象图中,以此类推,垃圾收集器通过跟列表的递推遍历,将能找到的所有可达对象,并形成一个可达对象图。同时那些不可达对象则被认为是可回收对象,垃圾收集器接着运行垃圾收集进程来释放垃圾对象的内存空间。通常,这种手机算法称为:标记和清除收集算法。

2:非托管资源

    对于大部分的类型来说,只存在内存资源的分配与回收问题,因此CLR的处理已经能够满足这种需求,然而还有部分的类型不可避免的涉及访问其他非托管资源。常见的非托管资源包括数据库连接,文件句柄,网络连接,互斥体,COM对象,套接字,位图和GDI+对象等。

  GC全权负责了对托管堆的内存管理,而内存之外的资源,又该由谁来打理?在.net中,非托管资源的清理,主要有两种方式:Finalize方法和Dispose方法,这两种方法提供了在垃圾收集执行前进行资源清理的方法。Finalize方式,大致原理是:通过对自定义类型实现了一个Finalize方法在对象的内存回收之前通过调用来释放资源。而Dispose方法是在类中实现了IDisposeable接口,该接口定了显式释放由对象引用的所有非托管资源