在应用程序初始化的之前,所有等级的托管堆都是空的。当对象被初始化的时候,他们会按照初始化的先后顺序被放入等级为0的托管堆中。在托管堆中对象的存放是连续的,这样使得托管堆存取对象的速度很快,因为托管对不必对内存进行搜索。垃圾回收器中保存了一个指针指向托管堆中最后一个对象之后的内存空间。图一中显示了一个包含四个对象的0等级的托管堆。
图一 包含四个对象的托管堆
当0等级托管堆被对象填满后,例如候程序初始化了新的对象,使0等级托管堆的大小超过了256K,垃圾回收器会检查托管堆中的所有对象,看是否有对象可以回收。当开始回收操作时,如前面提到的,垃圾回收器会找出根节点和根节点直接或间接引用了的对象,然后将这些对象转移到1等级托管堆中,并将0等级托管堆的指针移到最开始的位置以清除所有的对象。同时垃圾回收器会压缩1等级托管堆以保证所有对象之间没有内存空隙。当1等级托管堆满了之后,会将对象转移到2等级的托管堆。
例如在图一之后,垃圾回收器开始回收对象,假定D对象将被回收,同时程序创建了E和F对象。这时候托管堆中的对象如图二所示。
图二 回收对象后的0等级和1等级托管堆
然后程序创建了新的对象G和H,再一次触发了垃圾回收器。对象E将被回收。这时候托管堆中的对象如图三所示。
生存期垃圾回收器的原则也有例外的情况。当对象的大小超过84K时,对象会被放入"大对象区"。大对象区中的对象不会被垃圾回收器回收,也不会被压缩。这样做是为了强制垃圾回收器只能回收小对象以提高程序的性能。
控制垃圾回收器 在.Net框架中提供了很多方法使开发人员能够直接控制垃圾回收器的行为。通过使用GC.Collect()或GC.Collect(int GenerationNumber)开发人员可以强制垃圾回收器对所有等级的托管堆进行回收操作。在大多数的情况下开发人员不需要干涉垃圾回收器的行为,但是有些情况下,例如当程序进行了非常复杂的操作后希望确认内存中的垃圾对象已经被回收,就可以使用上面的方法。另一个方法是GC.WaitForPendingFinalizers(),它可以挂起当前线程,直到处理完成器队列的线程清空该队列为止。
使用垃圾回收器最好的方法就是跟踪程序中定义的对象,在程序不需要它们的时候手动释放它们。例如程序中的一个对象中有一个字符串属性,该属性会占用一定的内存空间。当该属性不再被使用时,开发人员可以在程序中将其设定为null,这样垃圾回收器就可以回收该字符串占用的空间。另外,如果开发人员确定不再使用某个对象时,需要同时确定没有其它对象引用该对象,否则垃圾回收器不会回收该对象。
另外值得一提的是finalize()方法应该在较短的时间内完成,这是因为垃圾回收器给finalize()方法限定了一个时间,如果finalize()方法在规定时间内还没有完成,垃圾回收器会终止运行finalize()方法的线程。在下面这些情况下程序会调用对象的finalize()方法:
0等级垃圾回收器已满
程序调用了执行垃圾回收的方法
公共语言运行库正在卸载一个应用程序域
公共语言运行库正在被卸载
查看本文来源