.NET中的GC(Garbage Collection)是自动内存管理的关键部分,它负责识别并回收不再使用的对象,从而避免内存泄漏。在C#中,由于语言的特性,开发者无需手动管理内存,大部分对象的生命周期由GC自动控制。
托管代码和非托管代码是.NET框架中的两个基本概念。托管代码指的是由.NET运行时环境(CLR,Common Language Runtime)管理和执行的代码,例如C#、VB.NET等。在这种环境下,GC能够监控和管理对象的生命周期。而非托管代码则包括如C++/CLI、P/Invoke调用的本地API或特定于平台的代码,这些代码不受.NET运行时直接管理,因此GC无法对它们进行内存回收。
GC可以回收任何托管对象,但不包括非托管资源。非托管资源如文件流(FileStream)、数据库连接(connection)和COM组件等,需要开发者手动管理。例如,使用`using`语句或显式调用`Dispose()`方法来释放非托管资源,以确保它们在不再使用时被正确关闭和清理,防止资源泄露。
GC回收对象的时机是不确定的,因为它是根据系统的需求和内存压力自动触发的。通常,当内存分配达到一定阈值或系统需要释放内存时,GC会被启动。然而,开发者可以通过调用`GC.Collect()`强制执行垃圾回收,但这并不推荐,因为频繁地调用`GC.Collect()`可能会影响程序性能,尤其是在需要高性能的应用中。
在C#中,对象的生命周期始于`new`关键字创建实例时,结束于GC确定该对象不再可到达时。不可到达的对象是指没有任何活动的引用指向它们,这意味着这些对象可以被视为垃圾。当对象的析构函数(finalizer)被调用时,表明对象即将被回收。析构函数主要用于清理非托管资源,而不是托管对象。在析构函数中,通常会调用`Dispose()`方法来释放非托管资源。
在上述示例中,当按钮点击事件创建新对象并调用`GC.Collect()`时,如果对象仍然在作用域内(例如,仍在方法体内),GC不会回收它们,因为它们仍然是可达的。只有当对象超出作用域并且不再有引用时,GC才会在合适的时机回收它们,并调用析构函数。在程序退出时,所有剩余的托管资源通常会被释放。
总结来说,C#中的GC是一个自动的内存管理系统,主要负责托管对象的生命周期管理。开发者需要关注的是非托管资源的释放,通过适当的机制如`Dispose()`和析构函数来确保非托管资源的正确清理。了解并合理运用GC的工作原理,有助于编写出高效且资源友好的.NET应用程序。