一、在程序运行时由clr管理内存分配(memory allocation),程序启动时,操作系统会为每一个线程申请一个独立的栈内存,用于存储方法的局部变量、参数和返回值等;clr会为进程申请一个连续的内存空间作为托管堆内存,用于存储引用类型对象和类型对象等;

  1.托管堆主要包含两部分:存储引用类型对象的gc堆(gc heap)和存储类型对象的加载堆(loader heap),其中gc堆分为小对象堆(small object heap,soh,<85000byte的对象)和大对象堆(larage object heap,log,>=85000byte的对象);加载堆不受gc控制,生命周期从创建到应用程序域被卸载;
  2.clr在申请托管堆内存时,会维护一个指向下一个对象内存地址的指针,当在托管堆中分配新对象时,会通过该指针添加值来为对象分配所需的内存,因此在托管堆中分配内存和在栈内存中分配内存的速度基本一样快;

  二、在栈上分配值类型的变量时,如果值类型所占的空间不足当前系统的位数时,会分配当前系统位数的内存空间,例如在64位系统上分配的int类型的变量占用64位,即8个字节;对于引用类型地址也是这样,即32位系统上引用类型地址占用4个字节,64位系统上则占用8个字节;

//在64位系统上测试:
unsafe
{
    int num1 = 10;
    int num2 = 20;
    myclass myclass = new myclass();
    int num3 = 30;
    console.writeline((int)&num1);
    console.writeline((int)&num2); //与num1地址相差8个字节
    console.writeline((int)&num3); //与num2地址相差16个字节
}

  

  三、在使用运算符new创建引用类型对象或装箱操作等在托管堆上创建对象时,clr所做的主要操作有:

  1.计算类及所有基类中声明的所有实例字段所需要的字节数,还有两个开销成员(overhead member)的字节数:同步块索引(sync block index)和类型对象指针(type object pointer);
※同步块索引为线程同步提供支持,也被称为对象头字节(object header word);
※类型对象指针存储该对象的类型对象所在的内存地址,也被称为方法表指针(method table pointer);
  2.从托管堆中分配计算所得到的字节数,所有字节初始化为0;
  3.初始化对象的类型对象指针和同步块索引;
  4.调用对应的实例构造函数,初始化实例字段,执行自定义构造函数中的其它操作;
※优先调用直接基类中的实例构造函数,直接基类中的实例构造函数又会调用其直接基类中的实例构造函数,最终最先调用的是基类system.object中的实例构造函数;
  5.返回新建对象的引用;

  注:32位系统中,同步块索引和类型对象指针分别占4个字节,占用的总空间大小会进行4字节倍数的对齐,同时即使类型定义中没有实例字段,也会至少占用4个字节,即最小占用内存空间12字节;
  注:64位系统中,同步块索引和类型对象指针分别占8个字节,占用的总空间大小会进行8字节倍数的对齐,同时即使类型定义中没有实例字段,也会至少占用8个字节,即最小占用内存空间24字节;

  注:clr在托管堆中连续分配多个对象时,这些对象在内存中也是连续存储的;

  注:可以使用windbg查看具体的内存分配情况;

  注:clr高度优化了托管堆上内存的分配和释放,大多数情况下,在堆内存上分配类实例与在栈内存上分配结构实例的性能并无显著差异;

 

 

如果您觉得阅读本文对您有帮助,请点一下“推荐”按钮,您的认可是我写作的最大动力!

作者:minotauros
出处:

本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。