.net CLR GC 基础和算法摘记

tag: clr gc

本系列为CRL via C#的节选

newobj(IL)的步骤
  • 计算类型及其基类所有字段所需要的字节总数
  • 在前边字节的基础上加上两个开销数量:类型对象指针(4Byte),同步索引块(4Byte)。
  • CLR检查物理内存是否足够。如果足够,则新对象被放置在NewObjPtr所指的托管堆位置.接着构造器被调用(NewObjPtr被传递给this)。NewObjPtr指向下一处可用内存。newobj指令返回新对象地址。

CLR与C运行时内存分配的对比:
  • C运行时操作链表,查找足够大的内存块并返回,之后修改链表。连续分配对象的内存可能不连续。
  • CLR的内存分配则只是简单的地址加新对象的偏移量,效率极高,甚至堪比线程堆栈。连续分配对象的内存连续。

连续内存的优势:
  • 进程的工作集相对较小
  • 方法中使用的对象也更可能同时在CPU缓存中驻留

其它一些说明:
  • GC保证了托管堆的内存无限的假设。
  • new操作符通过检查NewObjPtr+Offset是否超界来触发GC(基于上边的简单模型)。
  • GC只有在第0代对象充满时才会触发。
  • GC有相当的性能开销。

GC算法:
  • 根:程序中的一个存储位置,包含了一个指向引用类型对象的(托管堆)内存地址。可以是static字段、方法参数或局部变量。注意,只有引用类型的变量才是根,值类型变量不是。
  • GC不会手机根所指向的对象(存在引用)。
  • GC可以遍历线程调用栈,检查每个方法的内部表来确定方法内部的根。
  • GC在所有类型对象中迭代执行以得到静态字段的根组。
  • GC开始执行时,假设各个对象都可以收集(没有标记)。
  • 之后,GC开始标记(marking)操作:如果发现根引用了一个对象,就在该对象的同步块索引字段上标记一位(可能递归根所引用的对象,如果存在的话)。
  • 继续标记下一个根,如果已经被标记,则停止次根的标记活动(提高效率并避免环)
  • 线性遍历垃圾(未被标记的)内存块,直到找到较大的连续内存块,则把之前的非垃圾对象搬移到该地址以压缩托管堆。
  • 搬移对象需要修改指向原对象地址的CPU寄存器内容。
  • 使NewObjPtr指向最后一个非垃圾对象之后。

基于以上理论,GC需要能够识别程序中的根,而且能找到所有对象的指针。CLR托管堆总能够知道对象的实际类型,从而使用metadata判断对象的哪些成员引用着其它的对象。由于C++可以对指针进行任意转型,因此就无法知道指针所引用对象的确切类型,继而无法GC。

你可能感兴趣的:(C++,c,算法,.net,C#)