CLR要求所有的资源都从托管堆(managed heap)分配。这个堆和c运行时堆非常相似,只是你永远不从托管堆中删除对象——应用程序不需要的对象会自动删除。这自然引起一个问题:“托管堆如何知道应用程序不再用一个对象?”我稍后就会回答这个问题。
目前使用的垃圾回收算法有好几种。每种算法都针对特定环境进行优化,能提供这种环境下最佳的性能。本章关注的是Microsoft .NET Framework 的CLR所采用的垃圾回收算法。先从最基本的概念讲起。
进程初始化,CLR要保留一块连续的地址空间,这个地址空间最初并没有对应的物理存储空间。这个地址空间就是托管堆。托管堆还维护着一个指针,我把它称为NextObjPtr. 它指向下一个对象在堆中的分配位置。刚开始的时候NextObj设为保留地址空间的基本地址。
IL指令newobj用于创建一个对象。许多语言都提供一个new操作符,它导致编译器在方法的IL代码中生成一个newobj指令。newobj指令将导致CLR执行以下步骤。
1,计算类型(及其所有基类型)的字段所需要的字节数。
2,加上对象的开销所需的字节数。每个对象都有两个开销字段:一个是类型对象指针,和一个同步块索引。对于32位应用程序,这两个字段各自需要32位,所以每个对象要增加8字节。对于64位应用程序,这每个字段各自需要64位,所以每个对象要增加16字节。
3,CLR检查保留区域是否能够提供分配对象所需的字节数,如有必要就提交存储。如果托管堆有足够的可用空间,对象会被放入。注意对象是在NextObjPtr指针指向的地址放入的,并且为它分配字段会被清零。接着,调用类型的实例构造器(为this参数传递NextObjPtr), IL指令newobj将返回对象的地址。就在地址返回之前,NextObjPtr 指针的值会加上对象占据的字节数,这样会得到一个新值,它指向下一个对象放入托管堆时的地址。
垃圾回收器的工作原理
应用程序调用new 操作符创建对象时,可能没有足够的地址空间来分配该对象。托管堆将对象需要的字节数加到NextObjPtr指针的地址上来检测这种情况。如果结果值超过了地址空间的末尾,表明托管堆已满,必须执行一次垃圾回收。