在 .NET 中,垃圾回收(GC)是全自动、非确定性、分代式、压缩式的内存自动释放机制。
面试只要能把下面 9 句话讲清,就足以让面试官点头。
面试只要能把下面 9 句话讲清,就足以让面试官点头。
1. 谁负责
CLR 的 Garbage Collector (GC) 独占线程,程序员无法主动启动或终止,只能“建议”(
GC.Collect
默认会被延迟/忽略)。2. 回收什么
只回收托管堆(Managed Heap)上的内存;
非托管资源(句柄、Socket、DB 连接)必须自己释放(
非托管资源(句柄、Socket、DB 连接)必须自己释放(
Dispose
/Finalizer
)。3. 分代策略(Generational)
托管堆逻辑上分成三代:
-
Gen0 – 最新分配,每次 GC 必扫;
-
Gen1 – 0 次幸存,充当“缓冲带”;
-
Gen2 – 多次幸存,_full GC 才扫;
对象越大(≥85 000 B)直接进入 LOH(Large Object Heap),Gen2 才检查,不压缩(.NET 4.5 起可可选压缩)。
4. 根(Roots)与可达性
GC 从全局/栈/寄存器里的 根引用出发,图遍历标记“可达”;
不可达 = 垃圾,标记结束后统一回收或压缩。
不可达 = 垃圾,标记结束后统一回收或压缩。
5. 工作流程(单线程简化版)
-
Suspend 托管线程 →
-
Mark 可达对象 →
-
Relocate 更新引用地址 →
-
Compact 把幸存对象挤到连续空间 →
-
Resume 线程并修复指针。
Server GC 模式下,每个 NUMA 节点一个专用 GC 线程,可与用户线程并行(Background GC)。
6. 触发时机(非确定)
-
Gen0 预算用完;
-
分配大对象 LOH;
-
系统物理内存告急;
-
显式
GC.Collect
(不推荐); -
AppDomain / 进程卸载。
7. 程序员能干预什么
-
IDisposable + using —— 尽早释放非托管资源;
-
GC.SuppressFinalize(this) —— 告诉 CLR 跳过 Finalizer,避免一次额外晋升;
-
GCSettings.LatencyMode —— 短时间切换到低延迟(游戏、金融行情);
-
GCHandle —— 人工保持引用或钉住(Pin)内存供非托管代码访问。
8. 性能指标
-
% Time in GC
= GC CPU 时间 / 进程 CPU 时间,< 5 % 健康; -
Gen0/Gen1/Gen2 回收次数比值,理想 100:10:1;
-
Promotion Rate 过高说明内存压力或对象生存期过长。
9. 常见面试快问快答
表格
问题 | 一句话标准答案 |
---|---|
GC 会立即执行吗? | 不会,非确定性,由 CLR 按需触发。 |
能在构造器里 GC.Collect 强制释放吗? | 语法可以,实际会被延迟或忽略,别这么写。 |
结构体会被 GC 吗? | 不会,值类型随栈帧或容器释放,不受 GC 管理。 |
什么对象一定进入 Gen2? | LOH 大对象 和多次幸存的普通对象。 |
Dispose 与 Finalizer 区别? | Dispose 显式、确定性;Finalizer 是GC 后的保底,调用时机不可控。 |