• [你必须知道的.NET]第十八回:对象创建始末(上)


    本文将介绍以下内容:

    • 对象的创建过程
    • 内存分配分析
    • 内存布局研究

       

    1. 引言

    了解.NET的内存管理机制,首先应该从内存分配开始,也就是对象的创建环节。对象的创建,是个复杂的过程,主要包括内存分配和初始化两个环节。例如,对象的创建过程可以表示为:

                FileStream fs = new FileStream(@"C:"temp.txt"FileMode.Create);

    通过new关键字操作,即完成了对FileStream类型对象的创建过程,这一看似简单的操作背后,却经历着相当复杂的过程和周折。

    本篇全文,正是对这一操作背后过程的详细讨论,从中了解.NET的内存分配是如何实现的?

    2. 内存分配

    关于内存的分配,首先应该了解分配在哪里的问题。CLR管理内存的区域,主要有三块,分别为:

    ·       线程的堆栈,用于分配值类型实例。堆栈主要由操作系统管理,而不受垃圾收集器的控制,当值类型实例所在方法结束时,其存储单位自动释放。栈的执行效率高,但存储容量有限。

    ·       GC堆,用于分配小对象实例。如果引用类型对象的实例大小小于85000字节,实例将被分配在GC堆上,当有内存分配或者回收时,垃圾收集器可能会对GC堆进行压缩,详情见后文讲述。

    ·       LOHLarge Object Heap)堆,用于分配大对象实例。如果引用类型对象的实例大小不小于85000字节时,该实例将被分配到LOH堆上,而LOH堆不会被压缩,而且只在完全GC回收时被回收。

    本文讨论的重点是.NET的内存分配机制,因此下文将不加说明的以GC堆上的分配为例来展开。关于值类型和引用类型的论述,请参见[第八回:品味类型---值类型与引用类型(上)-内存有理]

    了解了内存分配的区域,接着我们看看有哪些操作将导致对象创建和内存分配的发生,关于实例创建有多个IL指令解析,主要包括:

    ·       newobj,用于创建引用类型对象。

    ·       ldstr,用于创建string类型对象。

    ·       newarr,用于分配新的数组对象。

    ·       box,在值类型转换为引用类型对象时,将值类型字段拷贝到托管堆上发生的内存分配。

        在上述论述的基础上,下面从堆栈的内存分配和托管堆的内存分配两个方面来分别论述.NET的内存分配机制。

    2.1 堆栈的内存分配机制

    对于值类型来说,一般创建在线程的堆栈上。但并非所有的值类型都创建在线程的堆栈上,例如作为类的字段时,值类型作为实例成员的一部分也被创建在托管堆上;装箱发生时,值类型字段也会拷贝在托管堆上。

    对于分配在堆栈上的局部变量来说,操作系统维护着一个堆栈指针来指向下一个自由空间的地址,并且堆栈的内存地址是由高位到低位向下填充。以下例而言:

            public static void Main()
            {
                
    int x = 100;
                
    char c = 'A';
            }

       假设线程栈的初始化地址为50000,因此堆栈指针首先指向50000地址空间。代码由入口函数Main开始执行,首先进入作用域的是整型局部变量x,它将在栈上分配4Byte的内存空间,因此堆栈指针向下移动4个字节,则值100将保存在49997~50000单位,而堆栈指针表示的下一个自由空间地址为49996,如图所示:

    接着进入下一行代码,将为字符型变量c分配2Byte的内存空间,堆栈指针向下移动2个字节至49994单位,值’A’会保存在49995~49996单位,地址的分配如图:

    最后,执行到Main方法的右括号,方法体执行结束,变量xc的作用域也随之结束,需要删除变量xc在堆栈内存中的值,其释放过程和分配过程刚好相反:首先删除c的内存,堆栈指针向上递增2个字节,然后删除x的内存,堆栈指针继续向上递增4个字节,程序执行结束,此时的内存状况为:

        其他较复杂的分配过程,可能在作用域和分配大小上有所不同,但是基本过程大同小异。栈上的内存分配,效率较高,但是内存容量不大,同时变量的生存周期随着方法的结束而消亡。

    未完待续:托管堆的内存分配机制必要的补充说明,近期发布,敬请关注。 

    参考文献

    (USA)Joe Duffy, Professinal .NET Framework 2.0

    (USA)Don Box, Essiential .NET

    (MSDN)Hanu Kommalapati and Tom Christian, Drill Into .NET Framework Internals to See How the CLR Creates Runtime Objects, http://msdn.microsoft.com/msdnmag/issues/05/05/JITCompiler/default.aspx 

  • 相关阅读:
    COCOS学习笔记--单点触控
    [ExtJS5学习笔记]第九节 Extjs5的mvc与mvvm框架结构简单介绍
    STM32F103 TIM1输出PWM设置
    【转】Android--UI之ProgressBar--不错
    【传】玩转Android---UI篇---ImageButton(带图标的按钮)
    【转】Android--多线程之Handler--不错
    【转】MFC获取程序目录路径方法
    【转】蓝牙4.0BLE cc2540 usb-dongle的 SmartRF Packet Sniffer 抓取数据方法--不错
    【转】windows常用消息大全(系统消息、通告消息、用户消息)
    【转】VC的MFC中重绘函数的使用总结(整理)
  • 原文地址:https://www.cnblogs.com/ShaYeBlog/p/3200780.html
Copyright © 2020-2023  润新知