• C#/.NET堆与栈

    堆与栈都是内存空间的一部分,其中,堆又可以分为托管堆和非托管堆。托管堆和栈由 CLR 管理。

    对托管堆中的一部分——GC 堆中不用的对象进行释放就是垃圾回收的主要工作,而托管堆的其他部分,和开发者关系相对没有那么大。对非托管堆的管理则需要由开发者完成。

    在 CLR 开始执行第一行代码之前,它会先建立三个程序域:系统域、共享域以及默认的一个应用程序域(AppDomain 类型的一个实例)。

    其中,开发者无法直接操作系统域和共享域,但 AppDomain 类型的实例可以有多个。

    对于简单的程序,例如控制台程序,第一个默认域的名称就是执行文件的全名,例如 abc.exe。可以使用 CreateDomain 方法创建更多的应用程序域。

    每一个 AppDomain 的实例都有自己的加载堆,下面就会介绍加载堆到底是什么。

    这里的堆(heap)是托管堆(managed heap)的简称。顾名思义,它由 CLR 进行管理。

    它是在运行程序时,CLR 申请的一块内存空间。它基于进程,属于进程内存空间的一部分。

    这块空间可以划分为下面几个主要部分:

    • (至少)三个程序域,以及它们自带的加载堆和其他零部件。
    • GC 堆(GC heap):垃圾收集器的处理对象。它分为 0, 1, 2 代三块区域,越高代的堆大小越大。

    大对象堆(large object heap)是 2 代堆的一部分,它存放超过 85KB 大小的对象。

    因为大对象堆里面的对象太大,移动代价过高,所以微软设计的意图是直接将它提升到 2 代,避免升代移动引起的性能损失。

    其中,加载堆(loader heap)存在于每一个程序域中,存放 CLR 自己的类型系统以及用户定义的类型对象。

    不同域的加载堆存放的对象不同。另外,AppDomain 的加载堆也存放静态对象,由于静态对象是全局的,不会成为垃圾,所以加载堆不受垃圾收集器管辖。

    加载堆又可以分为高频堆(大小为 32KB),低频堆(大小为 8KB)等。顾名思义,高频堆存放的是 CLR 认为访问次数可能较多的对象,例如类型方法表等。

    下图简单展示了托管堆的结构。

    托管堆结构简图

更多...

加载中...