C#程序的生命周期:从CLR加载到GC回收
·
C#程序的生命周期:从CLR加载到GC回收
1. CLR加载阶段
当操作系统启动C#程序时:
- 程序集加载:CLR加载主程序集(.exe)及其依赖项(.dll)
- 元数据解析:读取程序集中的元数据表(类型、方法、字段等)
- 应用程序域创建:建立隔离的执行环境
- 入口点定位:找到
Main()方法入口
流程示意:
$ \text{OS启动} \rightarrow \text{CLR初始化} \rightarrow \text{程序集加载} \rightarrow \text{元数据解析} \rightarrow \text{AppDomain创建} $
2. JIT编译阶段
CLR通过即时编译转换MSIL代码:
// MSIL代码示例(简化)
ldarg.0 // 加载参数
call void Console::WriteLine(string)
ret
- 方法桩(Stub):首次调用方法时插入跳转指令
- 编译过程:
$ \text{MSIL} \xrightarrow{\text{JIT}} \text{原生机器码} $ - 代码缓存:编译后的机器码存入内存,后续调用直接执行
3. 执行阶段
- 线程管理:CLR创建主线程执行
Main() - 内存分配:
- 栈:存储值类型、方法参数(自动回收)
- 托管堆:存储引用类型对象
- 异常处理:基于异常表实现
try/catch/finally
内存分配示例:
$$ \text{对象大小} = \text{对象头} + \text{类型指针} + \sum{\text{字段大小}} $$
4. 垃圾回收(GC)阶段
当堆内存不足或系统触发时:
GC核心过程
- 挂起线程:暂停所有托管线程
- 标记阶段:
- 从GC根(静态字段、局部变量等)开始遍历
$ \text{可达对象} = \bigcup_{\text{根}\in\text{GC Roots}}{\text{引用链}} $
- 从GC根(静态字段、局部变量等)开始遍历
- 压缩阶段(可选): $$ \text{新地址} = \text{基址} + \sum_{i=0}^{n-1}{\text{对象}_i\text{大小}} $$
- 修复指针:更新对象引用地址
- 恢复线程:继续程序执行
分代回收机制
| 代 | 对象特征 | 回收频率 |
|---|---|---|
| Gen0 | 新创建对象 | 高频 |
| Gen1 | 幸存Gen0的对象 | 中频 |
| Gen2 | 长期存活对象 | 低频 |
5. 程序终止
- 正常退出:
Main()执行完毕 - 资源清理:
// Finalizer示例 class Resource { ~Resource() { /* 清理非托管资源 */ } } - CLR卸载:释放所有托管堆内存,关闭应用程序域
完整生命周期图示
$$ \boxed{\begin{array}{c} \text{CLR加载} \ \downarrow \ \text{JIT编译} \ \downarrow \ \text{执行} \rightarrow \text{内存分配} \ \downarrow \ \text{GC触发} \begin{cases} \text{标记} \ \text{清理} \ \text{压缩} \end{cases} \ \downarrow \ \text{程序终止} \end{array}} $$
更多推荐
所有评论(0)