GC.TryStartNoGCRegion并非开启无GC模式,而是申请托管堆净增内存预算,超限直接抛OutOfMemoryException;必须成对调用EndNoGCRegion,且仅适用于可精确预估、毫秒级、零分配的确定性场景。
GC.TryStartNoGCRegion 为什么不能随便用
它不是“开启无 GC 模式”的开关,而是向运行时申请一段内存预算,在此期间若分配超出预算,会直接抛出 OutOfMemoryException,而不是触发 GC。低延迟场景下误用反而导致崩溃,比 GC 延迟更致命。
- 必须配合
GC.EndNoGCRegion()成对调用,否则后续所有 GC 都可能被阻塞或失败 - 申请的字节数是「托管堆净增」上限,不包括已存在对象、大对象堆(LOH)分配、非托管内存
- 如果运行时无法预留足够连续内存(例如堆已碎片化),
TryStartNoGCRegion直接返回false,不抛异常也不生效 - 在 .NET 6+ 中,若启用了
Server GC且线程未绑定到特定 GC 节点,行为可能不稳定
适合什么低延迟代码段
仅适用于可精确预估内存用量、执行时间极短(毫秒级)、且绝对不允许 GC 干扰的确定性逻辑,比如高频行情解码、实时音频 buffer 处理、确定性物理步进计算。
- 典型模式:先用
GC.GetTotalMemory(true)+ 预估峰值分配量,再尝试锁定 - 不能包含任何可能触发 JIT 编译、反射、字符串插值(
$"...")、LINQ 构建新集合的操作 - 禁止调用任何可能隐式分配的 API,例如
Exception.ToString()、DateTime.Now(某些实现会分配字符串)、Guid.NewGuid() - 推荐只在
unsafe上下文或纯数值计算中使用,避免引用类型分配
实际调用时的关键参数和陷阱
GC.TryStartNoGCRegion(long totalSize, bool disallowFullBlockingGC = false) 的两个参数都影响成败。