.Net Discovery系列之七 深入理解.Net垃圾收集机制2011-01-19 博客园 缘清第一节.垃圾回收算法与完整收集(Full GC)垃圾收集器就是跟踪所有被引用到的对象,整理对象不再被引用的对象,回收相应的内存,它使用“标记与清除”算法,分两步回收对象:Step 1.Mark-Sweep :从应用程序的root出发,利用相互引用关系,遍历其在Heap上动态分配的所有对象,指明需要回收的对象,标记出那些存活的对象,予以标记。Step 2.Compact: 对内存中存活的对象进行移动,修改它们的指针,使之在内存中连续,这样空闲的内存也就连续了,即完成了内存释放工作,也解决了内存碎片问题,这个过程也可以成为指针的压缩。垃圾收集器一般将托管堆中的对象分为3代,这可以通过调用GC.MaxGeneration得知,对象按照存在时间长短进行分代,最短的分在第0代,最长的分在第2代,第2代中的对象往往是比较大的,第二代空间被称作Large Object Heap,对于2代对象的回收,与第0、1代回收方式相比最大的不同在于,没有了指针移动的压缩过程。

图1 对象的回收如上图所示,左边的区域为第一次GC时的结构,需要注意的是GC标记的是那些存活的对象,而不是需要回收的,所以第一次回收,对象B、D没有被标记,所以被回收了,之后GC移动了对象内存指针,使空间连续。接下来看中间的部分,第二次GC开始了,C对象没有被标记,所以被回收了,接下来A、D、F三个对象被压缩,形成连续的内存空间,并且形成了第1、2、3代区域。接下来看最右边的部分,D对象没有被标记,由于D对象处于第2代中,所以回收D对象后,GC没有启动压缩步骤,因为对于大对象的指针移动,资源耗费成本很高。对于第2代的GC称为Full GC,新分配的对象在第0代(0代空间最大长度通常为256K),按地址顺序分配,它们通常是一些局部变量;第1代(1代空间最大长度通常为2 MB)是经过0代垃圾收集后仍然驻留在内存中的对象,它们通常是一些如表单,按钮等对象;第2代是经历过几次垃圾收集后仍然驻留在内存中的对象,它们通常是一些应用程序对象。可见一次Full GC需要的资源是最多的,可能是几秒或十几秒。托管堆的内存分配以段(Segment)为单位,CLR启动时通常为GC Heap创建2个段,分别用来存储第0、1代对象和第2代对象,以下是通过Windbg工具查看到的GC Heap情况:

图2 WinDbg 查看GC Heap情况可以看出,GC堆被分成了两个段,三代,每代起始地址十进制差值为12。在理解方面需要注意的是,GC回收的是程序中的引用类型,值类型是保存在堆栈之中,当值类型对象出了作用域后会自动释放内存----即弹栈,不需要垃圾收集器管理。