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 认为访问次数可能较多的对象,例如类型方法表等。
下图简单展示了托管堆的结构。
发表评论