c#

位置:IT落伍者 >> c# >> 浏览文章

你必须知道的.NET:内存分配


发布日期:2019年12月08日
 
你必须知道的.NET:内存分配

在分析内存分配时应该先了解关于堆栈的区别

堆的分配向高地址扩展而栈的分配向低地址扩展

内存分配

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

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

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

    publicclassVIPUser:User

    {

    //分配Byte

    publicboolisVip;

    publicboolIsVipUser()

    {

    returnisVip;

    }

    staticvoidMain(string[]args)

    {

    //分配内存空间和初始化操作

    VIPUseraUser;

    //将对象引用赋给aUser变量建立aUser和VIPUser的关联

    aUser=newVIPUser();

    //Q:类型的分配的字节数?

    //就本类而言需要Byte但是实例对象所占的字节总数还要加上对象附加成员所需的字节数其中包括附加成员TypeHandle和SyncBlockIndex共个字节在托管堆上分配的字节总数为字节而堆上的内存块总是按照Byte的倍数进行分配因此本类中将分配字节的地址空间

    //最后调用对象构造器进行对象初始化操作完成创建

    //构造过程

    //a构造VIPUser类型的Type对象主要包括静态字段方法表实现的接口等并将其分配在上文提到托管堆的LoaderHeap上

    //b初始化aUser的两个附加成员TypeHandle和SyncBlockIndex将TypeHandle指针指向LoaderHeap上的MethodTableCLR将根据TypeHandle来定位具体的Type将SyncBlockIndex指针指向SynchronizationBlock的内存块用于在多线程环境下对实例对象的同步操作

    //c调用VIPUser的构造器进行实例字段的初始化实例初始化时会首先向上递归执行父类初始化直到完成SystemObject类型的初始化然后再返回执行子类的初始化直到执行VIPUser类为止以本例而言初始化过程为首先执行SystemObject类再执行User类最后才是VIPUser类最终newobj分配的托管堆的内存地址被传递给VIPUser的this参数并将其引用传给栈上声明的aUser

    aUserisVip=true;

    ConsoleWriteLine(aUserIsVipUser());

    //上述过程基本完成了一个引用类型创建内存分配和初始化的整个流程

    }

    }

    publicclassUserInfo

    {

    //分配个字节

    privateIntage=;

    //分配个字节

    privatecharlevel=A;

    }

    publicclassUser

    {

    //分配byte

    privateIntid;

    //保存了UserInfo的引用占用Byte

    //仅是一个引用(指针)保存在线程的堆栈上占用Byte的内存空间用于保存user对象的有效地址现在试图对user的任何操作将抛出NullReferenceException

    privateUserInfouser;

    }

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

在了解内存分配之前 首先了解一下三个概念

TypeHandle类型句柄指向对应实例的方法表每个对象创建时都包含该附加成员并且占用个字节的内存空间我们知道每个类型都对应于一个方法表方法表创建于编译时主要包含了类型的特征信息实现的接口数目方法表的slot数目等

SyncBlockIndex用于线程同步每个对象创建时也包含该附加成员它指向一块被称为Synchronization Block的内存块用于管理对象同步同样占用个字节的内存空间

NextObjPtr由托管堆维护的一个指针用于标识下一个新建对象分配时在托管堆中所处的位置CLR初始化时NextObjPtr位于托管堆的基地址

继承本质论

    //Birdbird创建的是一个对象的引用而newBird()是创建Bird对象分配内存和初始化操作然后将对象引用赋给bird变量也就是简历bird和Bird之间的关联

    Birdbird=newBird();

    //从继承的角度来分析CLR在运行时如何执行对象的创建过程

    //首先是字段的创建字段的存储顺序由上到下排列最高层类的字段排在最前面

    //方法表的创建是类第一次加载到AppDomain时完成的在对象创建时只是将其附加成员TypeHandle指向方法列表LoaderHeap上的地址将对象与其动态方法列表相关联起来因此方法表示先于对象存在的

    Chickench=newChicken();

               

上一篇:详解C#中相等运算符重载可能造成的陷阱

下一篇:微软:.NET Framework 4新增LocalDB支持