.NET教程 - 编译器(Compiler)
更新记录
转载请注明出处:
2022年10月6日 发布。
2022年10月5日 从笔记迁移到博客。
CLR
CLR 管理内存的区域
主要有三块,分别为
线程的堆栈
用于分配值类型实例
堆栈主要由操作系统管理,而不受垃圾收集器的控制
当值类型实例所在方法结束时,其存储单位自动释放
栈的执行效率高,但存储容量有限
GC 堆
用于分配小对象实例
如果引用类型对象的实例容量占用小于 85000 字节
实例将被分配在 GC 堆上,当有内存分配或者回收时
垃圾收集器可能会对 GC 堆进行压缩
LOH(Large Object Heap)堆
用于分配大对象实例
如果引用类型对象的实例容量占用不小于 85000 字节时
该实例将被分配到 LOH 堆上,而 LOH 堆不会被压缩
而且只在完全 GC 回收时被回收
堆栈的内存分配机制
对于值类型来说,一般创建在线程的堆栈上
但并非所有的值类型都创建在线程的堆栈上
例如作为类的字段时,值类型作为实例成员的一部分也被创建在托管堆上
装箱发生时,值类型字段也会拷贝在托管堆上
对于分配在堆栈上的局部变量来说,操作系统维护着一个堆栈指针来指向下一个自由空间的地址
并且堆栈的内存地址是由高位到低位向下填充
实例
对于分配在堆栈上的局部变量来说,操作系统维护着一个堆栈指针来指向下一个自由空间的地址
并且堆栈的内存地址是由高位到低位向下填充
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 方法的右括号,方法体执行结束,变量 x 和 c 的作用域也随之结束
需要删除变量 x 和 c 在堆栈内存中的值,其释放过程和分配过程刚好相反
首先删除 c 的内存,堆栈指针向上递增 2 个字节,然后删除 x 的内存
堆栈指针继续向上递增 4 个字节,程序执行结束,此时的内存状况为
其他较复杂的分配过程,可能在作用域和分配大小上不同,但是基本过程大同小异
栈的内存分配,效率较高,但是内存容量不大,同时变量的生存周期随着方法的结束而消亡
托管堆的内存分配机制
引用类型的实例分配于托管堆上,而线程栈却是对象生命周期开始的地方
对 32 位处理器来说,应用程序完成进程初始化后
CLR 将在进程的可用地址空间上分配一块保留的地址空间,
它是进程(每个进程可使用 4GB)中可用地址空间上的一块内存区域
但并不对应于任何物理内存,这块地址空间即是托管堆
引用类型的实例分配于托管堆上,而线程栈却是对象生命周期开始的地方
对 32 位处理器来说,应用程序完成进程初始化后
CLR 将在进程的可用地址空间上分配一块保留的地址空间
它是进程(每个进程可使用 4GB)中可用地址空间上的一块内存区域
但并不对应于任何物理内存,这块地址空间即是托管堆
托管堆又根据存储信息的不同划分为多个区域
其中最重要的是垃圾回收堆(GC Heap)和加载堆(Loader Heap)
GC Heap 用于存储对象实例,受 GC 管理
Loader Heap 又分为 High-Frequency Heap、Low-Frequency Heap 和 Stub Heap
不同的堆上又存储不同的信息
Loader Heap 最重要的信息就是元数据相关的信息,也就是 Type 对象
每个 Type 在 Loader Heap 上体现为一个 Method Table(方法表)
而 Method Table 中则记录了存储的元数据信息
例如基类型、静态字段、实现的接口、所有的方法等等。Loader Heap 不受 GC 控制
其生命周期为从创建到 AppDomain 卸载
IL 指令解析
对象创建和内存分配相关指令
主要包括:
newobj 用于创建引用类型对象
ldstr 用于创建 string 类型对象
newarr 用于分配新的数组对象
box 在值类型转换为引用类型对象时,将值类型字段拷贝到托管堆上发生的内存分配
本文来自博客园,作者:重庆熊猫,转载请注明原文链接:https://www.cnblogs.com/cqpanda/p/16755113.html