在 C# 中,“类的对象的生命周期”是指从对象被创建(分配内存、初始化)到它被垃圾回收器(GC)回收、内存被释放的整个过程。理解这个过程,对写出高性能、无内存泄漏的程序非常关键。
✅ 一句话总结:
对象的生命周期 = 创建 → 使用 → 不再被引用 → GC 标记 → 回收 → 内存释放
🔍 分阶段详解:
🧠 关键概念补充:
✅ GC Roots 包括:
全局静态变量
当前线程栈上的局部变量
CPU 寄存器中的引用
正在使用的
GCHandle
⚠️ 注意:
GC 是非确定的:你无法预测对象什么时候被回收。
Finalizer 不保证及时执行:不要依赖它来释放关键资源。
推荐使用
Dispose()
模式(即IDisposable
)来释放非托管资源(如文件句柄、数据库连接)。
🧪 示例代码:
class MyClass : IDisposable
{
public MyClass()
{
Console.WriteLine("对象创建");
}
~MyClass() // 终结器(不推荐依赖)
{
Console.WriteLine("终结器被调用");
}
public void Dispose()
{
Console.WriteLine("Dispose 被调用");
GC.SuppressFinalize(this); // 告诉 GC 不用再调用终结器
}
}
class Program
{
static void Main()
{
MyClass obj = new MyClass();
obj.Dispose(); // 手动释放资源
obj = null; // 取消引用
GC.Collect(); // 强制触发 GC(仅用于测试)
GC.WaitForPendingFinalizers();
Console.WriteLine("程序结束");
}
}
✅ 总结一句话(面试可用):
C# 中对象的生命周期由 CLR 管理,从
new
创建开始,到不再被任何根引用后,由 GC 在某个不确定时间点回收内存;非托管资源应通过IDisposable
显式释放。
如果你有具体场景(比如:内存泄漏、Dispose 没调用、GC 不回收等),我可以帮你分析。
下面给出一段最小可运行的 C# 控制台程序,把“对象的一生”全部打印出来。 代码里每行都加了中文标注,运行后屏幕上的输出就是生命周期的时间线。
using System;
namespace LifeCycleDemo
{
// 1. 定义一个“会说话”的类,方便我们观察每一步
class DemoObject : IDisposable
{
private readonly string _id;
// 构造阶段:对象诞生
public DemoObject(string id)
{
_id = id;
Console.WriteLine($"【构造】 {_id} 对象在托管堆上分配,构造函数执行完毕。");
}
// 模拟“干活”
public void DoWork()
{
Console.WriteLine($"【使用】 {_id} 正在干活,this={GetHashCode()}");
}
// 显式释放资源(IDisposable 接口)
public void Dispose()
{
Console.WriteLine($"【Dispose】{_id} 被显式释放,非托管资源已清理。");
GC.SuppressFinalize(this); // 告诉 GC“别再跑终结器”
}
// 终结器(Finalizer)—— 备胎,万一忘记 Dispose
~DemoObject()
{
Console.WriteLine($"【终结器】 {_id} 被终结,对象即将被回收。");
}
}
class Program
{
static void Main()
{
Console.WriteLine("=== 对象生命周期演示开始 ===\n");
// 2. 创建:new 导致在托管堆分配
DemoObject obj = new DemoObject("A1");
obj.DoWork(); // 3. 使用
// 4. 主动 Dispose,释放非托管资源
obj.Dispose();
// 5. 断开引用:对象变成“不可达”
obj = null;
// 6. 强制 GC(仅演示用,生产代码不要这么写)
Console.WriteLine("\n【GC】 强制触发垃圾回收...");
GC.Collect(0, GCCollectionMode.Forced, blocking: true, compacting: true);
GC.WaitForPendingFinalizers(); // 等终结器跑完
Console.WriteLine("\n=== 对象生命周期演示结束 ===");
}
}
}
把文件保存为 LifeCycleDemo.cs
,编译运行:
dotnet run
典型输出(已对齐):
=== 对象生命周期演示开始 ===
【构造】 A1 对象在托管堆上分配,构造函数执行完毕。
【使用】 A1 正在干活,this=54267266
【Dispose】A1 被显式释放,非托管资源已清理。
【GC】 强制触发垃圾回收...
=== 对象生命周期演示结束 ===
注意: 由于我们在 Dispose()
里调用了 GC.SuppressFinalize(this)
,终结器不会执行,所以看不到“【终结器】”这一行。 如果把 Dispose()
那行注释掉再运行,就能观察到“【终结器】”输出,证明对象真的被 GC 回收了。
这样,“创建 → 使用 → 显式释放 → 失去引用 → GC 回收” 整个生命周期就完整跑通并打印出来了。