.NET 垃圾回收浅解
在说明垃圾回收的实现机制之前,先说明一下垃圾回收存在的背景。
垃圾回收器(GC)是.NET平台中一个很重要的组成部分,.NET垃圾回收机制降低了编写程序的复杂程度,使程序员不用耗费精力去处理析构,成功的将内存管理从程序的编写时,脱离到运行时。
一、析构函数
析构函数的作用主要是释放类在构造函数中以及类生命周期中所获取的资源,比如需要释放互斥锁、由操作符NEW所分配的对象内存等。当然析构函数也不只限于释放资源,一般析构函数可以执行在最后一次操作对象后所需要执行的任何操作。
二、关于垃圾回收
在.NET FRAMEWORK框架中,内存中的资源(二进制信息)分为“托管资源”和“非托管资源”,其中托管资源必须受到CLR的管理(如强类型安全检查),非托管资源需要手动释放。
托管资源分别存放在两个地方,一个是“堆栈”,另外一个是“托管堆”。值类型和引用类型的引用存放在“堆栈”中,而引用类型所代表的对象存在于“托管堆”中。
三、垃圾回收算法
CLR中的每个对象有两个开销字段:类型对象指针,同步块索引。
(1)、对于对象生存期的管理,有的系统采用“引用计数算法”,而对于微软的COM用的就是此算法。具体算法就是每个对象都在维护着一个内存字段,用来统计有多少个位置在使用该对象,当某一个位置不在使用该对象后,该内存字段中的计数就减一,直至该字段最后为0,当值为0时,该对象就可以从内存中删除掉了。
但是该算法最大的问题就是处理不好循环引用。
(2)、CLR使用的算法为“引用跟踪算法”,此算法只关心引用类型的变量,因为只有这种变量才能引用堆上的对象。可以将引用类型的变量称为根。
此算法是当CLR进行GC时,首先暂停所有线程,以防止线程CLR在检查期间访问对象并更改其状态。
A、CLR遍历堆中的所有对象,并将同步块索引字段中的一位设为0,这表明所有对象可以被删除。
B、查看所有活动根,并查看它们引用了哪些对象。如果一个根包括NULL,则忽略此根继续检查下一个根。
C、如果某根引用了堆上的对象,则会标记此对象,在该对象的同步块索引中的位设为1。此对象被标记后,CLR会检查这个对象中的根,标记它们引用的对象。若发现对象已经标记,则不再重新检查该对象,也就避免了因为循环引用而产生死循环。
D、检查完毕后,堆中的对象要么被标记,要么未被标记,然后CLR进入GC的“压缩”阶段。(需要注意的是,静态字段引用的对象会一直存在,直到用于加载类型的APPDOMAIN卸载为止。内在泄漏的一个重要原因就是静态字段引用某个对象集合,然后不停的向对象集合添加数据项。所以,要尽量避免使用静态字段。)
四、代的机制
CLR的GC是基于代的垃圾回收器,对于垃圾回收做了以下假设:
1、对象越新,生存期越短。
2、对象越老,生存期越长。
3、回收堆的一部分,速度快于回收整个堆。
五、垃圾回收触发条件
1、当CLR检测到第0代超过内存预算时,则触发一次GC。
2、代码显式调用GC的静态方法Collect。大多时候要避免使用此方法。从第2代开始回收。
3、当使用CreateMemoryResourceNotification或者QueryMemoryResourceNotification来监视系统的总体内存使用情况时,如果Windows报告内存低,则CLR会强制垃圾回收以释放死对象,减小进程工作集。
4、CLR卸载APPDOMAIN时,CLR会认为一切都不是根。
5、CLR正在关闭,Windows将会回收进程中的全部内存。
六、垃圾回收模式
有两种基本GC模式:工作站、服务器。应用程序默认使用以“工作站”GC模式运行。若想使用服务器回收器,可以在配置文件中添加gcServer元素(gcServer=true)。
另外还有两种子模式:并发(默认)和非并发。一般并发垃圾回收器消耗的内存通常比使用非并发垃圾回收器多。若要使用非并发回收器,可在配置文件中创建gcConcurrent元素(enable=false)
七、需要特殊清理的类型
针对一些本机资源的终结,CLR除了回收对象内存之前,还需要在Finalize中释放本机资源,如下所示:
1 public class sometype() 2 { 3 ~sometype()//此为一个finalize方法 4 }
需要注意的是,finalize方法的执行时间是控制不了的。同时,CLR不保证多个finalize方法的调用顺序,在finalize方法中最好不要访问定义了finalize方法的其他类型的对象。调用静态方法也要小心。由于此方法是为释放本机资源而设计的,所以要谨慎使用。
如果想创建封装了本机资源的类型时,需要继承Safehandle。
另外一种释放本机资源的方法是Dispose,调用此方法只是控制这个清理动作的发生时间。同时,调用此方法并不会将托管对象从托管堆中删除。若决定显示调用Dispose方法,建议将调用放在一个异常处理finally块中。