调用 GC.Collect()
能触发 Full GC 吗?
能,但不保证是 Full GC —— 默认情况下
GC.Collect()只回收所有代(0、1、2),而 .NET 的 Server GC 模式下,它**通常会触发 Full GC**;Workstation GC(尤其并发模式)则可能跳过第 2 代,除非显式指定。关键看是否带参数和运行时配置。
如何确保触发 Full GC(含第 2 代 + 大对象堆 LOH)
必须显式传入
GC.MaxGeneration并配合
GC.WaitForPendingFinalizers()(如果关心 finalizer 执行),否则回收可能未完成就继续执行。
GC.Collect(GC.MaxGeneration, GCCollectionMode.Forced, blocking: true, compacting: true)(.NET 5+ 推荐) 旧版本(.NET Framework / .NET Core 3.1)用
GC.Collect(GC.MaxGeneration)+
GC.WaitForPendingFinalizers()若需强制压缩大对象堆(LOH),.NET 5+ 必须设
compacting: true;之前版本无法直接压缩 LOH Server GC 下,该调用影响整个 GC heap;Workstation GC 下只影响当前线程所属 heap(多 heap 场景下不全覆盖)
GC.Collect(
GC.MaxGeneration,
GCCollectionMode.Forced,
blocking: true,
compacting: true);
GC.WaitForPendingFinalizers();
为什么生产环境几乎不该手动调用 Full GC
手动触发 Full GC 会暂停所有托管线程(Stop-The-World),延迟可能达数百毫秒甚至秒级,尤其在大堆或高负载时。CLR 的自适应 GC 策略远比人工判断更可靠。
常见误用场景:内存“看起来没释放”就调用 —— 实际可能是对象仍被引用、finalizer 队列积压、或本就不该由 GC 回收(如 unmanaged resource)GC.CollectionCount(2)可查第 2 代回收次数,用于监控而非干预 真正需要干预时,优先检查
IDisposable是否漏调
Dispose()、是否存在事件订阅泄漏、缓存未清理等
调试阶段验证 Full GC 是否生效
仅限开发/诊断,别放生产代码里。用
GC.GetTotalMemory(forceFullCollection: true)强制同步回收并获取估算值,再对比前后差值;更准的方式是用 dotnet-trace 或 PerfView 抓 GC 日志,看是否出现
GEN2类型事件。
GC.GetTotalMemory(true)会触发一次 full collection(等价于
GC.Collect(GC.MaxGeneration)),但不等待 finalizer PowerShell 中可用
dotnet-counters monitor -p <pid> --counters System.Runtime</pid>实时观察
% Time in GC和
Gen 2 Collections注意:
GC.GetTotalMemory()返回的是估算值,不是精确堆大小
long before = GC.GetTotalMemory(false); GC.Collect(GC.MaxGeneration, GCCollectionMode.Forced, true, true); GC.WaitForPendingFinalizers(); long after = GC.GetTotalMemory(true);.NET 的 GC 行为高度依赖运行时版本、GC 模式(Server/Workstation)、是否启用并发、以及堆大小。手动触发 Full GC 不是“清内存开关”,而是对 GC 机制的一次强干预——得清楚自己在打断什么,以及打断之后谁来兜底。
