c# 如何用C#代码触发一次GC回收 Full GC

来源:这里教程网 时间:2026-02-21 17:39:26 作者:

调用
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 机制的一次强干预——得清楚自己在打断什么,以及打断之后谁来兜底。

相关推荐