首页 > 代码库 > 托管堆和垃圾回收
托管堆和垃圾回收
一、托管堆基础
1,访问一个资源(文件、内存缓冲区、屏幕空间、网络连接、数据库资源等)所需的步骤
①调用IL指令newobj,为代表资源的类型分配内存(一般使用c# new操作符来完成)
②初始化内存,设置资源的初始状态并使资源可用。类型的实例构造器负责设置初始状态
③访问类型的成员来使用资源(有必要可以重复)
④摧毁资源的状态以进行清理
⑤释放内存。垃圾回收器独自负责这一步
2,从托管堆分配资源
初始化进程时,CLR划出一个地址空间区域作为托管堆,一个区域被非垃圾对象填满后,CLR会分配更多的区域(32位进程最多能分配1.5GB,64为进程最多能分配8TB)。CLR还要维护一个指针(NextObjPtr),该指针指向下一个对象在堆中的分配位值。刚开始的时候,NextObjPtr设为地址空间区域的基地址。
3,C#的new操作符导致CLR执行以下步骤
①计算类型的字段(以及从基类型继承的字段)所需的字节数。
②加上对象的开销所需的字节数。每个对象都有两个开销字段:类型对象指针和同步快索引(32位:两个字段各需32位,所以每个对象要增加8字节。64位:每个字段各需64位,所以每个对象要增加16字节)(int=4字节;long=8字节)
③CLR检查区域中是否有分配对象所需的字节数。如果托管堆有足够的可用空间,就在NextObjPtr指针指向的地址处放入对象,为对象分配的字节会被清零。接着调用类型的构造器(为this参数传递NextObjPtr),new操作符返回对象的引用。就在返回这个引用之前,NextObjPtr指针的值会加上对象占用的字节数来得到一个新值,即下一个对象放入托管堆是的地址
4,垃圾回收算法
CLR使用一种引用跟踪算法。引用跟踪算法只关心引用类型的变量,因为只有这种变量才能引用堆上的对象,我们将所有引用类型的变量都称为根。
①CLR开始GC时,首先暂停进程中的所有线程(这样可以防止线程在CLR检查期间访问对象并更改其状态)
②CLR进入GC标记阶段(这个阶段,CLR遍历堆中所有对象,将同步块索引字段中的一位设为0。这表明所有的对象都应该删除)
③CLR检查所有活动根(根为null,则CLR忽略这个根),查看他们引用了那些对象。如果引用了堆上的对象,CLR都会标记那个对象(将对象的同步块索引中的位设置为1)
④检查完毕后,堆中的对象要么标记。要么未标记。已标记的对象不能被垃圾回收,因为至少有一个根在引用它,我们说这些对象时可达的
⑤进入GC的压缩阶段,在这个阶段,CLR对堆中已标记的对象进行“乾坤大挪移”,压缩所有幸存下来的对象,使它们占用连续的内存对象
⑥压缩之后,根现在的引用还是原来的位置,而非移动之后的位置。所以作为压缩阶段的一部分,CLR还要从每个根减去所引用的对象在内存中的偏移的字节数。这样就能保证根还是引用和之前一样的对象;只是对象在内存中换了位置
5,垃圾回收和调试
托管堆和垃圾回收