.net 垃圾收集器会为我调用 IDisposable.Dispose 吗?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/45036/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Will the Garbage Collector call IDisposable.Dispose for me?
提问by Orion Edwards
The .NET IDisposable Patternimpliesthat if you write a finalizer, and implement IDisposable, that your finalizer needs to explicitly call Dispose. This is logical, and is what I've always done in the rare situations where a finalizer is warranted.
.NET IDisposable 模式意味着如果您编写终结器并实现 IDisposable,则终结器需要显式调用 Dispose。这是合乎逻辑的,也是我在需要终结器的极少数情况下所做的。
However, what happens if I just do this:
但是,如果我这样做会发生什么:
class Foo : IDisposable
{
public void Dispose(){ CloseSomeHandle(); }
}
and don't implement a finalizer, or anything. Will the framework call the Dispose method for me?
并且不要实现终结器或任何东西。框架会为我调用 Dispose 方法吗?
Yes I realise this sounds dumb, and all logic implies that it won't, but I've always had 2 things at the back of my head which have made me unsure.
是的,我意识到这听起来很愚蠢,所有逻辑都暗示它不会,但我脑子里总是有两件事让我不确定。
Someone a few years ago once told me that it would in fact do this, and that person had a very solid track record of "knowing their stuff."
The compiler/framework does other 'magic' things depending on what interfaces you implement (eg: foreach, extension methods, serialization based on attributes, etc), so it makes sense that this might be 'magic' too.
几年前有人告诉我,它实际上可以做到这一点,而且那个人在“了解他们的东西”方面有着非常扎实的记录。
编译器/框架根据您实现的接口(例如:foreach、扩展方法、基于属性的序列化等)执行其他“魔术”操作,因此这也可能是“魔术”是有道理的。
While I've read a lot of stuff about it, and there's been lots of things implied, I've never been able to find a definitiveYes or No answer to this question.
虽然我已经阅读了很多关于它的东西,并且有很多暗示的东西,但我从来没有能够找到这个问题的明确是或否的答案。
采纳答案by Xian
The .Net Garbage Collector calls the Object.Finalize method of an object on garbage collection. By defaultthis does nothingand must be overidden if you want to free additional resources.
.Net 垃圾收集器在垃圾收集时调用对象的 Object.Finalize 方法。通过默认情况下这并没有什么,而且必须overidden如果您要释放更多的资源。
Dispose is NOT automatically called and must be explicitycalled if resources are to be released, such as within a 'using' or 'try finally' block
如果要释放资源,则不会自动调用 Dispose ,例如在“using”或“try finally”块中,必须显式调用
see http://msdn.microsoft.com/en-us/library/system.object.finalize.aspxfor more information
有关详细信息,请参阅http://msdn.microsoft.com/en-us/library/system.object.finalize.aspx
回答by Cory Foy
I want to emphasize Brian's point in his comment, because it is important.
我想在他的评论中强调 Brian 的观点,因为它很重要。
Finalizers are not deterministic destructors like in C++. As others have pointed out, there is no guarantee of when it will be called, and indeed if you have enough memory, if it will everbe called.
终结器不是像 C++ 中那样的确定性析构函数。正如其他人所指出的那样,没有什么时候会被称为保证,而事实上,如果你有足够的内存,如果将永远被调用。
But the bad thing about finalizers is that, as Brian said, it causes your object to survive a garbage collection. This can be bad. Why?
但是终结器的坏处是,正如 Brian 所说,它会导致您的对象在垃圾回收中幸存下来。这可能很糟糕。为什么?
As you may or may not know, the GC is split into generations - Gen 0, 1 and 2, plus the Large Object Heap. Split is a loose term - you get one block of memory, but there are pointers of where the Gen 0 objects start and end.
您可能知道也可能不知道,GC 分为几代 - Gen 0、1 和 2,以及大对象堆。拆分是一个松散的术语 - 您获得一个内存块,但是有第 0 代对象开始和结束位置的指针。
The thought process is that you'll likely use lots of objects that will be short lived. So those should be easy and fast for the GC to get to - Gen 0 objects. So when there is memory pressure, the first thing it does is a Gen 0 collection.
思考过程是您可能会使用许多寿命很短的对象。因此,对于 GC 而言,这些应该很容易且快速地到达 - Gen 0 对象。所以当有内存压力时,它做的第一件事就是 Gen 0 集合。
Now, if that doesn't resolve enough pressure, then it goes back and does a Gen 1 sweep (redoing Gen 0), and then if still not enough, it does a Gen 2 sweep (redoing Gen 1 and Gen 0). So cleaning up long lived objects can take a while and be rather expensive (since your threads may be suspended during the operation).
现在,如果这不能解决足够的压力,那么它会返回并执行第 1 代扫描(重做第 0 代),然后如果仍然不够,则执行第 2 代扫描(重做第 1 代和第 0 代)。因此清理长期存在的对象可能需要一段时间并且相当昂贵(因为您的线程可能在操作期间被挂起)。
This means that if you do something like this:
这意味着如果你做这样的事情:
~MyClass() { }
Your object, no matter what, will live to Generation 2. This is because the GC has no way of calling the finalizer during garbage collection. So objects that have to be finalized are moved to a special queue to be cleaned out by a different thread (the finalizer thread - which if you kill makes all kinds of bad things happen). This means your objects hang around longer, and potentially force more garbage collections.
无论如何,您的对象将存活到第 2 代。这是因为 GC 在垃圾回收期间无法调用终结器。因此,必须完成的对象被移动到一个特殊的队列中,以便由不同的线程(终结器线程 - 如果你杀死它会使各种不好的事情发生)清除。这意味着您的对象会停留更长时间,并可能会强制进行更多的垃圾收集。
So, all of that is just to drive home the point that you want to use IDisposable to clean up resources whenever possible and seriously try to find ways around using the finalizer. It's in your application's best interests.
因此,所有这些只是为了让您明白您希望尽可能使用 IDisposable 清理资源并认真尝试找到使用终结器的方法。这符合您的应用程序的最佳利益。
回答by Andrew
There's lots of good discussion already here, and I'm a little late to the party, but I wanted to add a few points myself.
这里已经有很多很好的讨论,我参加聚会有点晚了,但我想自己补充几点。
- The Garbage collecter will never directly execute a Dispose method for you.
- The GC willexecute finalizers when it feels like it.
- One common pattern that is used for objects that have a finalizer is to have it call a method which is by convention defined as Dispose(bool disposing) passing false to indicate that the call was made due to finalization rather than an explicit Dispose call.
- This is because it is not safe to make any assumptions about other managed objects while finalizing an object (they may have already been finalized).
- 垃圾收集器永远不会直接为您执行 Dispose 方法。
- GC会在需要时执行终结器。
- 用于具有终结器的对象的一种常见模式是让它调用一个方法,该方法按照惯例定义为 Dispose(bool disposing) 传递 false 以指示该调用是由于终结而不是显式的 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;
}
}
}
That's the simple version, but there are a lot of nuances that can trip you up on this pattern.
这是简单的版本,但有很多细微差别可能会让您陷入这种模式。
- The contract for IDisposable.Dispose indicates that it must be safe to call multiple times (calling Dispose on an object that was already disposed should do nothing)
- It can get very complicated to properly manage an inheritance hierarchy of disposable objects, especially if different layers introduce new Disposable and unmanaged resources. In the pattern above Dispose(bool) is virtual to allow it to be overridden so that it can be managed, but I find it to be error-prone.
- IDisposable.Dispose 的契约表明多次调用必须是安全的(在已经释放的对象上调用 Dispose 应该什么都不做)
- 正确管理一次性对象的继承层次结构会变得非常复杂,特别是如果不同的层引入了新的 Disposable 和非托管资源。在上面的模式中, Dispose(bool) 是虚拟的,以允许覆盖它以便可以对其进行管理,但我发现它容易出错。
In my opinion, it is much better to completely avoid having any types that directly contain both disposable references and native resources that may require finalization. SafeHandles provide a very clean way of doing this by encapsulating native resources into disposable that internally provide their own finalization (along with a number of other benefits like removing the window during P/Invoke where a native handle could be lost due to an asynchronous exception).
在我看来,完全避免使用直接包含一次性引用和可能需要终结的本机资源的任何类型要好得多。SafeHandles 提供了一种非常干净的方法,通过将本机资源封装到内部提供自己完成的一次性资源中(以及许多其他好处,例如在 P/Invoke 期间删除窗口,其中本机句柄可能因异步异常而丢失) .
Simply defining a SafeHandle makes this Trivial:
简单地定义一个 SafeHandle 使这变得微不足道:
private class SomeSafeHandle
: SafeHandleZeroOrMinusOneIsInvalid {
public SomeSafeHandle()
: base(true)
{ }
protected override bool ReleaseHandle()
{ return NativeMethods.CloseHandle(handle); }
}
Allows you to simplify the containing type to:
允许您将包含类型简化为:
class SomeObject : IDisposable {
SomeSafeHandle _SomeSafeHandle;
FileStream _SomeFileStream;
// Something useful here
public virtual void Dispose() {
_SomeSafeHandle.Dispose();
_SomeFileStream.Dispose();
}
}
回答by Matt Bishop
I don't think so. You have control over when Dispose is called, which means you could in theory write disposal code that makes assumptions about (for instance) the existence of other objects. You have no control over when the finalizer is called, so it would be iffy to have the finalizer automatically call Dispose on your behalf.
我不这么认为。您可以控制调用 Dispose 的时间,这意味着理论上您可以编写处理代码,假设(例如)其他对象的存在。您无法控制何时调用终结器,因此让终结器自动代表您调用 Dispose 是不确定的。
EDIT: I went away and tested, just to make sure:
编辑:我离开并进行了测试,只是为了确保:
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");
}
}
回答by Brian Leahy
Not in the case you describe, But the GC will call the Finalizerfor you, if you have one.
不是在您描述的情况下,但是 GC 会为您调用Finalizer,如果您有的话。
HOWEVER. The next garbage collection ,instead of being collected, the object will go into the finalization que, everything gets collected, then it's finalizer called. The next collection after that it will be freed.
然而。下一次垃圾收集,而不是被收集,对象将进入终结队列,一切都被收集,然后调用终结器。之后的下一个集合将被释放。
Depending on the memory pressure of your app, you may not have a gc for that object generation for a while. So in the case of say, a file stream or a db connection, you may have to wait a while for the unmanaged resource to be freed in the finalizer call for a while, causing some issues.
根据您的应用程序的内存压力,您可能有一段时间没有用于该对象生成的 gc。因此,在文件流或数据库连接的情况下,您可能需要等待一段时间才能在终结器调用中释放非托管资源一段时间,从而导致一些问题。
回答by penyaskito
No, it's not called.
不,它没有被调用。
But this makes easy to don't forget to dispose your objects. Just use the usingkeyword.
但这很容易不要忘记处理您的对象。只需使用using关键字。
I did the following test for this:
我为此做了以下测试:
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!");
}
回答by Rob Walker
回答by Joseph Daigle
The documentation on IDisposablegives a pretty clear and detailed explaination of the behavior, as well as example code. The GC will NOT call the Dispose()method on the interface, but it will call the finalizer for your object.
IDisposable上的文档对行为以及示例代码进行了非常清晰和详细的解释。GC 不会调用Dispose()接口上的方法,但会调用对象的终结器。
回答by Erick Sgarbi
The IDisposable pattern was created primarily to be called by the developer, if you have an object that implements IDispose the developer should either implement the usingkeyword around the context of the object or call the Dispose method directly.
IDisposable 模式主要是为了由开发人员调用而创建的,如果您有一个实现 IDispose 的对象,开发人员应该using围绕对象的上下文实现关键字或直接调用 Dispose 方法。
The fail safe for the pattern is to implement the finalizer calling the Dispose() method. If you don't do that you may create some memory leaks i.e.: If you create some COM wrapper and never call the System.Runtime.Interop.Marshall.ReleaseComObject(comObject) (which would be placed in the Dispose method).
该模式的故障安全是实现调用 Dispose() 方法的终结器。如果你不这样做,你可能会造成一些内存泄漏,即:如果你创建了一些 COM 包装器并且从不调用 System.Runtime.Interop.Marshall.ReleaseComObject(comObject)(它将被放置在 Dispose 方法中)。
There is no magic in the clr to call Dispose methods automatically other than tracking objects that contain finalizers and storing them in the Finalizer table by the GC and calling them when some clean up heuristics kick in by the GC.
除了跟踪包含终结器的对象并由 GC 将它们存储在终结器表中并在 GC 启动一些清理启发式时调用它们之外,clr 中没有任何魔法可以自动调用 Dispose 方法。

