值类型中的引用类型字段和引用类型中的值类型字段,其分配情况又是如何?
对于值类型嵌套引用类型的情况,引用类型变量作为值类型的成员变量,在堆栈上保存该成员的引用,而实际的引用类型仍然保存在 GC 堆上;对于引用类型嵌套值类型的情况,则该值类型字段将作为引用类型实例的一部分保存在 GC 堆上。
方法保存在 Loader Heap 的 MethodTable 中,那么方法调用时又是怎么样的过程?
MethodTable 中包含了类型的元数据信息,类在加载时会在 Loader Heap上创建这些信息,一个类型在内存中对应一份 MethodTable,其中包含了所有的方法、静态字段和实现的接口信息等。对象实例的 TypeHandle 在实例创建时,将指向 MethodTable 开始位置的偏移处(默认偏移 12Byte),通过对象实例调用某个方法时,CLR 根据 TypeHandle 可以找到对应的 MethodTable,进而可以定位到具体的方法,再通过 JIT Compiler 将 IL 指令编译为本地 CPU 指令,该指令将保存在一个动态内存中,然后在该内存地址上执行该方法,同时该 CPU 指令被保存起来用于下一次的执行。
在 MethodTable 中,包含一个 Method Slot Table,称为方法槽表,该表是一个基于方法实现的线性链表,并按照以下顺序排列:继承的虚方法,引入的虚方法,实例方法和静态方法。方法表在创建时,将按照继承层次向上搜索父类,直到 System.Object 类型,如果子类覆写了父类方法,则将会以子类方法覆盖父类虚方法。
静态字段的内存分配和释放,又有何不同?
静态字段也保存在方法表中,位于方法表的槽数组后,其生命周期为从创建到 AppDomain卸载。因此一个类型无论创建多少个对象,其静态字段在内存中也只有一份。静态字段只能由静态构造函数进行初始化,静态构造函数确保在类型任何对象创建前,或者在任何静态字段或方法被引用前执行。
结论
对象创建过程的了解,是从底层接触 CLR 运行机制的入口,也是认识.NET 自动内存管理的关键。通过本文的详细论述,关于对象的创建、内存分配、初始化过程和方法调用等技术都会建立一个相对全面的理解,同时也清楚的把握了线程栈和托管堆的执行机制。