.net 垃圾收集器会调用IDisposable.Dispose吗?
NET意味着,如果编写终结器并实现IDisposable,则终结器需要显式调用Dispose。 这是合乎逻辑的,并且是我在少数情况下经常做的事情,在这种情况下,终结器是有保证的 但是,如果我这样做会发生什么:.net 垃圾收集器会调用IDisposable.Dispose吗?,.net,dispose,idisposable,.net,Dispose,Idisposable,NET意味着,如果编写终结器并实现IDisposable,则终结器需要显式调用Dispose。 这是合乎逻辑的,并且是我在少数情况下经常做的事情,在这种情况下,终结器是有保证的 但是,如果我这样做会发生什么: class Foo : IDisposable { public void Dispose(){ CloseSomeHandle(); } } 不要实现终结器或任何东西。框架会为我调用Dispose方法吗 是的,我意识到这听起来很愚蠢,所有的逻辑都暗示它不会,但我总是有两件事在
class Foo : IDisposable
{
public void Dispose(){ CloseSomeHandle(); }
}
不要实现终结器或任何东西。框架会为我调用Dispose方法吗
是的,我意识到这听起来很愚蠢,所有的逻辑都暗示它不会,但我总是有两件事在我的脑后,让我不确定
虽然我已经读了很多关于它的东西,而且有很多隐含的东西,但我从来都找不到这个问题的肯定的或否定的答案。我不这么认为。您可以控制何时调用Dispose,这意味着您可以在理论上编写Dispose代码,以假设(例如)其他对象的存在。您无法控制何时调用终结器,因此无法让终结器代表您自动调用Dispose
编辑:我去做了测试,只是为了确保:
class Program
{
static void Main(string[] args)
{
Fred f = new Fred();
f = null;
GC.Collect();
GC.WaitForPendingFinalizers();
Console.WriteLine("Fred's gone, and he's not coming back...");
Console.ReadLine();
}
}
class Fred : IDisposable
{
~Fred()
{
Console.WriteLine("Being finalized");
}
void IDisposable.Dispose()
{
Console.WriteLine("Being Disposed");
}
}
不是你描述的那种情况, 但是如果您有终结器,GC将为您调用终结器 不过。下一次垃圾收集,而不是被收集,对象将进入finalization que,收集所有内容,然后调用finalizer。之后的下一个集合将被释放
根据应用程序的内存压力,您可能暂时没有生成该对象的gc。因此,在文件流或db连接的情况下,您可能需要等待一段时间,以便在终结器调用中释放非托管资源,从而导致一些问题。垃圾回收器调用垃圾回收上对象的Object.Finalize方法。默认情况下,这不做任何事情,如果您想释放额外的资源,必须避免 Dispose不会自动调用,如果要释放资源,例如在“using”或“try finally”块中,则必须显式调用Dispose 有关更多信息,请参见。不,不调用 但这样做很容易,不要忘记处理对象。只需使用
using
关键字即可
我为此做了以下测试:
class Program
{
static void Main(string[] args)
{
Foo foo = new Foo();
foo = null;
Console.WriteLine("foo is null");
GC.Collect();
Console.WriteLine("GC Called");
Console.ReadLine();
}
}
class Foo : IDisposable
{
public void Dispose()
{
Console.WriteLine("Disposed!");
}
GC将不调用dispose。它可能会调用终结器,但即使在所有情况下也不能保证这一点
有关处理此问题的最佳方法的讨论,请参阅此部分。上的文档对该行为以及示例代码给出了非常清晰和详细的解释。GC不会调用接口上的
Dispose()
方法,但会调用对象的终结器。我想强调Brian在评论中的观点,因为这很重要
终结符不是像C++那样的确定性析构函数。正如其他人所指出的,无法保证它何时被调用,事实上,如果你有足够的内存,它是否会被调用
但是终结器的坏处在于,正如Brian所说,它会导致对象在垃圾收集中存活。这可能很糟糕。为什么? 您可能知道,也可能不知道,GC被分为几代—第0代、第1代和第2代,以及大型对象堆。Split是一个松散的术语-您可以获得一块内存,但有一些指针指示Gen 0对象的开始和结束位置 思考的过程是,你可能会使用很多短期的对象。因此,对于GC来说,这些应该是容易和快速的,以获得-gen0对象。因此,当存在内存压力时,它所做的第一件事就是收集Gen 0 现在,如果这不能解决足够的压力,那么它会返回并进行第1代扫描(重做第0代),如果仍然不够,它会进行第2代扫描(重做第1代和第0代)。因此,清理长寿命对象可能需要一段时间,而且成本相当高(因为在操作过程中线程可能会挂起) 这意味着如果你这样做:~MyClass() { }
你的目标,不管怎样,都会活到第二代。这是因为GC在垃圾收集期间无法调用终结器。因此,必须完成的对象被移动到一个特殊的队列中,由另一个线程(终结器线程——如果杀死它,会导致各种不好的事情发生)清除。这意味着您的对象挂起的时间更长,并可能强制执行更多的垃圾收集
所以,所有这些只是为了让大家明白,您希望尽可能使用IDisposable来清理资源,并认真尝试找到使用终结器的方法。这符合应用程序的最佳利益。IDisposable模式的创建主要是为了由开发人员调用,如果您有一个实现IDispose的对象,则开发人员应该在对象的上下文中使用
关键字实现,或者直接调用Dispose方法
该模式的故障安全机制是实现调用Dispose()方法的终结器。如果不这样做,您可能会造成一些内存泄漏,例如:如果您创建了一些COM包装,并且从未调用System.Runtime.Interop.Marshall.ReleaseComObject(comObject)(将放置在Dispose方法中)
在clr中,除了跟踪包含终结器的对象并由GC将它们存储在终结器表中并在某些cl出现时调用它们之外,自动调用Dispose方法没有什么神奇之处
class SomeObject : IDisposable {
IntPtr _SomeNativeHandle;
FileStream _SomeFileStream;
// Something useful here
~ SomeObject() {
Dispose(false);
}
public void Dispose() {
Dispose(true);
}
protected virtual void Dispose(bool disposing) {
if(disposing) {
GC.SuppressFinalize(this);
//Because the object was explicitly disposed, there will be no need to
//run the finalizer. Suppressing it reduces pressure on the GC
//The managed reference to an IDisposable is disposed only if the
_SomeFileStream.Dispose();
}
//Regardless, clean up the native handle ourselves. Because it is simple a member
// of the current instance, the GC can't have done anything to it,
// and this is the onlyplace to safely clean up
if(IntPtr.Zero != _SomeNativeHandle) {
NativeMethods.CloseHandle(_SomeNativeHandle);
_SomeNativeHandle = IntPtr.Zero;
}
}
}
private class SomeSafeHandle
: SafeHandleZeroOrMinusOneIsInvalid {
public SomeSafeHandle()
: base(true)
{ }
protected override bool ReleaseHandle()
{ return NativeMethods.CloseHandle(handle); }
}
class SomeObject : IDisposable {
SomeSafeHandle _SomeSafeHandle;
FileStream _SomeFileStream;
// Something useful here
public virtual void Dispose() {
_SomeSafeHandle.Dispose();
_SomeFileStream.Dispose();
}
}