分代假设:读懂 .NET GC 回收策略的主线
很多人对 GC 的印象是"它是个黑盒,卡了就是 GC 的锅"。其实 .NET 的回收策略建立在一个非常朴素的观察上:绝大多数对象活得很短。方法里 new 的临时对象,出了作用域就是垃圾;只有少数配置、缓存之类的对象能活到最后。
既然如此,就没必要每次都扫全堆。托管堆被切成三代:新对象进 Gen0,熬过一次回收升 Gen1,再熬升 Gen2。回收 Gen0 便宜(对象少、引用少),回收 Gen2 贵(全堆扫)。这个设计的赌注是:大部分垃圾在 Gen0 就被收掉了,Gen2 的回收频率可以很低。

另有一块特殊区域叫大对象堆(LOH),超过 85KB 的对象直接进去,不经历年轻代。这带来一个非常实用的推论:频繁分配大数组/大字符串,等于把最贵的回收(Gen2/LOH)当日常。我们有个导出服务拼 10MB 的字符串报表,每次导出都诱发一次全堆回收——改成流式写出之后,GC 时间直接归零。
日常调优就三句话:
- Gen0/Gen1 回收频繁是正常现象,不用慌
- Gen2 回收频繁才有问题,先找大对象分配
GC.GetTotalMemory和性能计数器看趋势,别凭感觉
GC 不是敌人,分配习惯才是。把"朝生夕死"这条假设当设计原则来遵守——对象尽量小、尽量短命——GC 自然安静。