1.深入探索.NET框架内部了解CLR如何创建运行时对象 http://www.microsoft.com/china/MSDN/library/netFramework/netframework/JITCompiler.mspx?mfr=true 2.CLR 全面透彻解析--大型对象堆揭秘 http://msdn.microsoft.com/zh-cn/magazine/cc534993.aspx 3.CLR探索系列 http://www.cnblogs.com/lbq1221119/archive/2008/02/20/1059152.html 4.深入理解.NET 的JIT编译方式 http://blog.csdn.net/tanliyoung/archive/2007/05/04/1596021.aspx 5.垃圾回收技术的发展 http://www.cnblogs.com/jillzhang/archive/2006/11/03/549281.html
6.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指向最后一个非垃圾对象之后。
|