GC(Garbage Collector)垃圾回收机制

1. 垃圾回收概念

  • GC 是 C# 自动管理堆内存的一种机制。
  • 通过遍历堆上的对象,判断哪些对象没有被任何引用指向,将这些对象释放。
  • 垃圾:没有被任何变量或对象引用的数据。

2. GC 回收对象的范围

  • 只回收堆(Heap)上的对象,引用类型的数据。
  • 栈(Stack)上的值类型数据由系统自动管理,函数结束时自动销毁,不需要 GC。

3. 回收算法(常见)

  1. 引用计数(Reference Counting)
    每个对象维护引用计数,引用为 0 时回收。
  2. 标记清除(Mark Sweep)
    标记可达对象,未标记对象回收。
  3. 标记整理(Mark Compact)
    标记可达对象,回收不可达对象,并压缩堆空间。
  4. 复制集合(Copy Collection)
    将存活对象复制到新区域,释放原区域。

4. 分代垃圾回收

4.1 内存分代与触发条件

  • 堆被分为三代:0 代、1 代、2 代
    • 0 代(Gen 0):新分配对象
      • 触发条件:0 代空间满 → 自动触发 0 代垃圾回收
      • 回收逻辑:回收未被引用的对象;存活的对象晋升到 1 代
    • 1 代(Gen 1):短期存活对象
      • 触发条件:1 代空间满 → 自动触发 0 代 + 1 代垃圾回收
      • 回收逻辑:回收 0/1 代未被引用的对象;存活的对象晋升到 2 代
    • 2 代(Gen 2):长期存活或大对象(LOH,大于 85 KB)
      • 触发条件:2 代空间满 → 自动触发 0/1/2 代垃圾回收
      • 回收逻辑:回收所有代未被引用的对象

注:0、1 代空间较小,频繁 GC;2 代空间较大,GC 不频繁。

4.2 对象晋升

  • 当 0 代 GC 结束后,存活对象晋升到 1 代
  • 晋升到 1 代时,内存不一定连续,会有碎片存在。
  • 1 代 GC 时会尝试压缩堆,将碎片合并,使存活对象连续分配,提高空间利用率。
  • 类似地,2 代 GC 也会进行压缩,但由于 2 代很大且 GC 不频繁,所以通常延迟压缩。

4.3 大对象(LOH,>85 KB)

  • LOH 对象直接进入 2 代或大对象堆(Large Object Heap)。
  • 不做搬迁,因为搬迁成本高。
  • 2 代 GC 会清理无引用 LOH,但存活对象不会移动。

4.4 GC 压缩与碎片

  • 0 代空间通常很小,回收后立即重用,不需要压缩。
  • 1 代会在 GC 时进行压缩,整理存活对象的连续空间,减少碎片。
  • 2 代也可以压缩堆,但压缩操作成本高,只在必要时(比如内存紧张)才会触发。
  • 0/1/2 代在 GC 时,都会扫描根对象,标记存活对象,释放不可达对象。

4.5 值得注意

  1. 0 代晋升到 1 代时,不一定连续分配 → 对
  2. 1 代空间满时会 GC,并尝试压缩堆 → 对
  3. 2 代满时会 GC → 对
  4. 2 代 GC 后仍不够用 → 系统可能抛出 OutOfMemoryException
  5. 0/1/2 代压缩行为
    • 0 代:通常不压缩
    • 1 代:GC 时压缩
    • 2 代:GC 时可压缩,但成本高,非必要不做

5. GC 工作流程

  1. 标记阶段
    从根对象(静态字段、方法参数等)出发,标记所有可达对象。
  2. 清理/压缩阶段
    回收未标记对象,必要时移动可达对象并更新引用。

6. 手动触发 GC

  • 常用于加载场景或清理大量资源时:
GC.Collect();
posted @ 2025-12-11 15:43  高山仰止666  阅读(10)  评论(0)    收藏  举报