csharp:gc回收

C# 中的 GC 回收基础与注意事项

GC(垃圾回收) 是 .NET CLR 提供的一种自动内存管理机制,用来回收不再被使用的托管对象

简单理解:
当一个对象再也找不到引用它的路径时,GC 就会回收它。

特点:

  • 自动执行,无需手动 free
  • 只回收托管内存(Managed Heap)
  • 并非实时执行

GC 的核心判断标准是:

对象是否还能从 GC Root 被“访问到(Reachable)”

如果不能访问到,则对象被认为是垃圾。


GC Root 是 GC 查找引用的起点,只要对象能从这些地方被引用,就不会被回收。

常见 GC Root 包括:

  • 当前方法中的局部变量(栈上的引用)
  • 静态变量(static 字段)
  • 当前线程对象
  • 全局单例
  • 事件的发布者(非常重要)
  • 非托管代码(如 Rhino / COM / C++)持有的对象

  1. 从所有 GC Root 开始遍历
  2. 标记所有可达对象
  3. 未被标记的对象视为垃圾
  4. 释放其占用的内存

为了提高性能,.NET 使用分代回收(Generational GC)

代(Generation) 说明
Gen 0 新创建的对象
Gen 1 存活过一次回收
Gen 2 长时间存活的对象

说明:

  • 大部分对象会在 Gen 0 被回收
  • Gen 2 回收成本最高
  • 长期被引用的 UI / Panel 对象通常会进入 Gen 2

GC 不会回收以下内容:

  • 正在被引用的对象
  • 静态变量引用的对象
  • 被事件订阅的对象
  • 非托管资源(如文件句柄、GDI、显存)

在 C# 中:

事件 = 发布者持有订阅者的强引用

示例:

class Publisher
{
    public static event Action OnEvent;
}
 
class Subscriber
{
    public Subscriber()
    {
        Publisher.OnEvent += Handle;
    }
 
    void Handle() { }
}

问题:

  • Publisher 是 static(GC Root)
  • Subscriber 被事件引用
  • 即使外部不再使用 Subscriber,GC 也无法回收

解决方法:

  • 在对象生命周期结束时必须使用 -= 取消订阅

UI 对象通常具有:

  • 生命周期短
  • 创建 / 销毁频繁
  • 但容易被全局对象引用

常见问题来源:

  • static 事件
  • 文档级事件(如 RhinoDoc)
  • 单例管理器

推荐做法:

  • PanelShown 时订阅事件
  • PanelHidden / PanelClosing 时取消订阅

GC 不会自动释放非托管资源

对于以下资源:

  • 文件
  • Socket
  • GDI / 显卡资源
  • Rhino / COM 对象

必须实现 IDisposable:

class MyResource : IDisposable
{
    public void Dispose()
    {
        // 手动释放非托管资源
    }
}

使用方式:

using (var res = new MyResource())
{
    // 使用资源
}

Finalizer(~ClassName):

  • 由 GC 在不确定时间调用
  • 性能成本高
  • 不保证立即执行

不推荐滥用:

~MyClass()
{
    // 不可靠,且影响性能
}

推荐使用:

  • IDisposable + using
  • 明确的资源释放时机

GC.Collect():

  • 强制触发 GC
  • 会导致性能下降
  • 破坏 GC 的优化策略

结论:

  • ❌ 不推荐在业务代码中调用
  • ✅ 只在调试或内存分析场景下使用

  • C# 有 GC 就不会内存泄漏(错误)
  • 对象设为 null 就一定会回收(错误)
  • UI 关闭等于对象销毁(错误)
  • GC 会处理事件引用(错误)

什么时候必须关注 GC:

  • 插件开发(Rhino / Revit / Unity)
  • 长时间运行的程序
  • 使用 static / 单例 / 全局事件
  • UI 面板频繁创建和销毁

核心原则一句话:

让对象在不需要时,从所有 GC Root 中“断开引用”

该主题尚不存在

您访问的页面并不存在。如果允许,您可以使用创建该页面按钮来创建它。

  • csharp/gc回收.txt
  • 最后更改: 2025/12/30 15:03
  • 张叶安