对于一般的代码,我是否真的需要处理对象?我可以忽略它,还是在确保不再需要它时始终处理对象是个好主意?
对于一般的代码,我是否真的需要处理对象?我可以忽略它,还是在确保不再需要它时始终处理对象是个好主意?
在您使用对象后立即处理掉它。可处理的对象代表持有有价值资源的对象,而CLR并不知道这些资源。因此,GC也无法智能地决定何时回收可处理对象,从而释放其底层资源。
最终,GC会感到内存压力,并仅仅是因为偶然而非智能地回收您的对象。如果您不以确定性方式处理可处理的对象,则完全可能进入资源匮乏状态,即使几乎没有内存压力。
以下是一个快速示例,说明如何发生这种情况。假设底层资源是Win32句柄。这些很有限且相当小。您运行了一个创建大量Foo对象的操作。Foo对象实现IDisposable接口,负责创建和处理Win32句柄。它们不是手动释放的,并且通过一种巧合进入Gen2堆。该堆不经常被释放。随着时间的推移,足够多的Foo实例进入Gen2堆中,占用了所有可用的句柄。新的Foo对象因此无法创建,无论使用了多少内存。
事实上,为了释放句柄,需要在单个操作期间分配相当大的内存才能产生足够的压力来释放这些实例。
如果对象实现了IDisposable接口,在使用完后应该立即对其进行处理。最简单的方法是将其包含在using块中:
using (SqlCommand cmd = new SqlCommand(conn)) {
cmd.ExecuteNonQuery();
}
任何实现 IDisposable
接口的类型都应该始终调用 Dispose()
,因为通常它被用来表示该类型获取了非托管资源。这些资源特别重要且应尽早释放。正如其他人所提到的,使用 using
是首选方式。
如果你的类没有持有非托管资源,那么你可以不调用 Dispose 方法。但是,如果你的类持有一个非托管资源,例如需要删除的临时文件,则必须显式地调用 Dispose。
你可以通过在 Finalize 方法中编写释放代码来避免调用 Dispose,但这样你就依赖于垃圾回收器,因为你永远不确定垃圾回收器何时会完成你的对象。为了安全起见,如果你设计了一个持有非托管资源的类,可以在 Dispose 和 Finalize 方法中编写相同的对象释放代码,但如果这样做,请始终在 dispose 方法中使用 SuppressFinalize(),因为如果你的对象已经在终结队列上,它将防止调用 Finalize() 方法。
using (var conn = new SqlConnection(connString)) {}
'Using'块绝对是确保对象正确处理的最清洁和最健壮的方法。 'Using'块可以与任何实现IDisposable接口的对象一起使用。
"当你完成一个对象的使用后,你可以忘记它。只要它没有被任何地方引用,那么它就像消失了一样。当垃圾收集器感觉到需要释放内存时,它所占用的内存就会被释放。