简体   繁体   English

如果我不在笔对象上调用Dispose会发生什么?

[英]What happens if I don't call Dispose on the pen object?

What happens if I don't call Dispose on the pen object in this code snippet? 如果我不在此代码段中的pen对象上调用Dispose ,会发生什么?

private void panel_Paint(object sender, PaintEventArgs e)
{
    var pen = Pen(Color.White, 1);
    //Do some drawing
}

A couple of corrections should be made here: 这里应该做一些修正:

Regarding the answer from Phil Devaney: 关于Phil Devaney的答案:

"...Calling Dispose allows you to do deterministic cleanup and is highly recommended." “......调用Dispose允许您进行确定性清理,强烈推荐。”

Actually, calling Dispose() does not deterministically cause a GC collection in .NET - ie it does NOT trigger a GC immediately just because you called Dispose(). 实际上,调用Dispose()并不能确定性地导致.NET中的GC集合 - 即它不会因为您调用Dispose()而立即触发GC。 It only indirectly signals to the GC that the object can be cleaned up during the next GC (for the Generation that the object lives in). 它只向GC间接发出信号,表示在下一次GC期间可以清除对象(对象所在的生成)。 In other words, if the object lives in Gen 1 then it wouldn't be disposed of until a Gen 1 collection takes place. 换句话说,如果对象位于第1代,则在第1代收集发生之前不会将其处理掉。 One of the only ways (though not the only) that you can programmatically and deterministically cause the GC to perform a collection is by calling GC.Collect(). 可以通过编程和确定性地使GC执行集合的唯一方法(尽管不是唯一的方法)是调用GC.Collect()。 However, doing so is not recommended since the GC "tunes" itself during runtime by collecting metrics about your memory allocations during runtime for your app. 但是,建议不要这样做,因为GC会在运行时通过收集有关应用程序运行时内存分配的指标来“调整”自身。 Calling GC.Collect() dumps those metrics and causes the GC to start its "tuning" all over again. 调用GC.Collect()会转储这些指标并导致GC重新开始“调整”。

Regarding the answer: 关于答案:

IDisposable is for disposing unmanaged resources. IDisposable用于处理非托管资源。 This is the pattern in .NET. 这是.NET中的模式。

This is incomplete. 这是不完整的。 As the GC is non-deterministic, the Dispose Pattern, ( How to properly implement the Dispose pattern ), is available so that you can release the resources you are using - managed or unmanaged. 由于GC是非确定性的,因此可以使用Dispose Pattern( 如何正确实现Dispose模式 ),以便您可以释放您正在使用的资源 - 托管或非托管。 It has nothing to do with what kind of resources you are releasing. 它与您发布的资源无关 The need for implementing a Finalizer does have to do with what kind of resources you are using - ie ONLY implement one if you have non-finalizable (ie native) resources. 实现Finalizer的需要与您正在使用的资源类型有关 - 即,如果您具有非可终结(即本机)资源,则仅实现一个资源。 Maybe you are confusing the two. 也许你会混淆两者。 BTW, you should avoid implementing a Finalizer by using the SafeHandle class instead which wraps native resources which are marshaled via P/Invoke or COM Interop. 顺便说一句,你应该避免使用SafeHandle类来实现Finalizer,而不是包装通过P / Invoke或COM Interop封送的本机资源。 If you do end up implementing a Finalizer, you should always implement the Dispose Pattern. 如果最终实现了Finalizer,则应始终实现Dispose Pattern。

One critical note which I haven't seen anyone mention yet is that if disposable object is created and it has a Finalizer (and you never really know whether they do - and you certainly shouldn't make any assumptions about that), then it will get sent directly to the Finalization Queue and live for at least 1 extra GC collection . 我还没有看到任何人提到的一个重要注意事项是,如果创建了一次性对象并且它有一个Finalizer(并且你从未真正知道它们是否存在 - 并且你当然不应该对此做出任何假设),那么它将会直接发送到Finalization Queue并进行至少1次额外的GC收集

If GC.SuppressFinalize() is not ultimately called, then the finalizer for the object will be called on the next GC. 如果最终未调用GC.SuppressFinalize(),则将在下一个GC上调用该对象的终结器。 Note that a proper implementation of the Dispose pattern should call GC.SuppressFinalize(). 请注意,Dispose模式的正确实现应该调用GC.SuppressFinalize()。 Thus, if you call Dispose() on the object, and it has implemented the pattern properly, you will avoid execution of the Finalizer. 因此,如果在对象上调用Dispose()并且它已正确实现了模式,则将避免执行Finalizer。 If you don't call Dispose() on an object which has a finalizer, the object will have its Finalizer executed by the GC on the next collection. 如果不对具有终结器的对象调用Dispose(),则该对象将在下一个集合上由GC执行其Finalizer。 Why is this bad? 为什么这么糟糕? The Finalizer thread in the CLR up to and including .NET 4.6 is single-threaded. CLR中的Finalizer线程(包括.NET 4.6)是单线程的。 Imagine what happens if you increase the burden on this thread - your app performance goes to you know where. 想象一下如果你增加这个线程的负担会发生什么 - 你的应用程序性能会告诉你在哪里。

Calling Dispose on an object provides for the following: 在对象上调用Dispose可提供以下内容:

  1. reduce strain on the GC for the process; 减少GC对该过程的压力;
  2. reduce the app's memory pressure; 减少应用程序的内存压力;
  3. reduce the chance of an OutOfMemoryException (OOM) if the LOH (Large Object Heap) gets fragmented and the object is on the LOH; 如果LOH(大对象堆)被分割并且对象在LOH上,则减少OutOfMemoryException(OOM)的可能性;
  4. Keep the object out of the Finalizable and f-reachable Queues if it has a Finalizer; 如果对象具有Finalizer,则将对象保留在Finalizable和f-reachable Queues之外;
  5. Make sure your resources (managed and unmanaged) are cleaned up. 确保清理您的资源(托管和非托管)。

Edit : I just noticed that the "all knowing and always correct" MSDN documentation on IDisposable (extreme sarcasm here) actually does say 编辑 :我刚刚注意到IDisposable (这里极端讽刺)的“所有知道并且始终正确”的MSDN文档确实说

The primary use of this interface is to release unmanaged resources 此接口的主要用途是释放非托管资源

As anyone should know, MSDN is far from correct, never mentions or shows 'best practices', sometimes provides examples that don't compile, etc. It is unfortunate that this is documented in those words. 任何人都应该知道,MSDN远非正确,从未提及或显示“最佳实践”,有时提供不编译的示例等。不幸的是,这些都记录在这些文字中。 However, I know what they were trying to say: in a perfect world the GC will cleanup all managed resources for you (how idealistic); 但是,我知道他们想说的是:在一个完美的世界里,GC会为你清理所有管理资源(多么理想化); it will not, however cleanup unmanaged resources. 然而,它不会清理非托管资源。 This is absolutely true. 这绝对是真的。 That being said, life is not perfect and neither is any application. 话虽这么说,生活并不完美,也没有任何应用。 The GC will only cleanup resources that have no rooted-references. GC只会清理没有rooted-references的资源。 This is mostly where the problem lies. 这主要是问题所在。

Among about 15-20 different ways that .NET can "leak" (or not free) memory, the one that would most likely bite you if you don't call Dispose() is the failure to unregister/unhook/unwire/detach event handlers/delegates. 在.NET可以“泄漏”(或不释放)内存的大约15-20种不同方式中,如果不调用Dispose(),最有可能咬你的方法是取消注册/取消挂起/取消/拆除事件处理器/代表。 If you create an object that has delegates wired to it and you don't call Dispose() (and don't detach the delegates yourself) on it, the GC will still see the object as having rooted references - ie the delegates. 如果您创建一个具有与其连接的委托的对象,并且您没有在其上调用Dispose()(并且不自行分离委托),则GC仍会将该对象视为具有root权限的引用 - 即委托。 Thus, the GC will never collect it. 因此,GC永远不会收集它。

@joren's comment/question below (my reply is too long for a comment): @joren的评论/问题如下(我的回复太长,无法发表评论):

I have a blog post about the Dispose pattern I recommend to use - ( How to properly implement the Dispose pattern ). 我有一篇关于我建议使用的Dispose模式的博客文章 - ( 如何正确实现Dispose模式 )。 There are times when you should null out references and it never hurts to do so. 有些时候你应该删除引用,它永远不会伤害这样做。 Actually, doing so does do something before GC runs - it removes the rooted reference to that object. 实际上,这样做会在GC运行之前做一些事情 - 它会删除对该对象的root权限引用。 The GC later scans its collection of rooted references and collects those that do not have a rooted reference. GC稍后会扫描其根植入的集合,并收集那些没有根参考的参考。 Think of this example when it is good to do so: you have an instance of type "ClassA" - let's call it 'X'. 想想这个例子,当这样做是好的时候:你有一个类型为“ClassA”的实例 - 我们称之为'X'。 X contains an object of type "ClassB" - let's call this 'Y'. X包含一个“ClassB”类型的对象 - 让我们称之为'Y'。 Y implements IDisposable, thus, X should do the same to dispose of Y. Let's assume that X is in Generation 2 or the LOH and Y is in Generation 0 or 1. When Dispose() is called on X and that implementation nulls out the reference to Y, the rooted reference to Y is immediately removed. Y实现了IDisposable,因此,X应该做同样的事情来处理Y.假设X在第2代或LOH中,Y在第0代或第1代。当在X上调用Dispose()并且该实现为空时引用Y,立即删除对Y的有根引用。 If a GC happens for Gen 0 or Gen 1, the memory/resources for Y is cleaned up but the memory/resources for X is not since X lives in Gen 2 or the LOH. 如果Gen 0或Gen 1发生GC,则清除Y的内存/资源,但X的内存/资源不是因为X存在于Gen 2或LOH中。

The Pen will be collected by the GC at some indeterminate point in the future, whether or not you call Dispose . Pen将通过GC在未来某个不确定的点进行收集,你是否不叫Dispose

However, any unmanaged resources held by the pen (eg, a GDI+ handle) will not be cleaned up by the GC. 但是,笔不会清除笔所持有的任何非托管资源(例如,GDI +句柄)。 The GC only cleans up managed resources. GC仅清理托管资源。 Calling Pen.Dispose allows you to ensure that these unmanaged resources are cleaned up in a timely manner and that you aren't leaking resources. 调用Pen.Dispose可以确保及时清理这些非托管资源,并确保您不会泄漏资源。

Now, if the Pen has a finalizer and that finalizer cleans up the unmanaged resources then those said unmanaged resources will be cleaned up when the Pen is garbage collected. 现在,如果Pen有一个终结器并且终结器清理了非托管资源,那么当Pen被垃圾收集时,那些所说的非托管资源将被清除。 But the point is that: 但重点是:

  1. You should call Dispose explicitly so that you release your unmanaged resources, and 您应该显式调用Dispose以便释放非托管资源,并且
  2. You shouldn't need to worry about the implementation detail of if there is a finalizer and it cleans up the unmanaged resources. 如果有终结器并且它清理了非托管资源,您不必担心实现细节。

Pen implements IDisposable . Pen实现IDisposable IDisposable is for disposing unmanaged resources. IDisposable用于处理非托管资源。 This is the pattern in .NET. 这是.NET中的模式。

For previous comments on the this topic, please see this answer . 有关此主题的先前评论,请参阅此答案

The underlying GDI+ pen handle will not be released until some indeterminate time in the future ie when the Pen object is garbage collected and the object's finalizer is called. 在将来某些不确定的时间,即Pen对象被垃圾收集并调用对象的终结器时,底层GDI +笔柄将不会被释放。 This might not be until the process terminates, or it might be earlier, but the point is its non-deterministic. 这可能不会在流程终止之前,或者可能更早,但重点是它是非确定性的。 Calling Dispose allows you to do deterministic cleanup and is highly recommended. 调用Dispose允许您进行确定性清理,强烈建议使用。

If you really want to know how bad it is when you don't call Dispose on graphics objects you can use the CLR Profiler, available free for the download here. 如果您真的想知道当您不在图形对象上调用Dispose时有多糟糕,可以使用CLR Profiler,可在此处免费下载 In the installation folder (defaults to C:\\CLRProfiler ) is CLRProfiler.doc which has a nice example of what happens when you don't call Dispose on a Brush object. 在安装文件夹(默认为C:\\ CLRProfiler)中是CLRProfiler.doc,它有一个很好的例子,说明当你不在Brush对象上调用Dispose时会发生什么。 It is very enlightening. 这很有启发性。 The short version is that graphics objects take up a larger chunk of memory than you might expect and they can hang around for a long time unless you call Dispose on them. 简短的版本是图形对象占用了比你想象的更大的内存块,它们可以长时间闲置,除非你在它们上面调用Dispose。 Once the objects are no longer in use the system will, eventually, clean them up, but that process takes up more CPU time than if you had just called Dispose when you were finished with the objects. 一旦对象不再使用,系统最终将清理它们,但是当您完成对象时刚刚调用Dispose时,该进程占用的CPU时间更多。 You may also want to read up on using IDisposable here and here . 您可能还想阅读此处此处使用IDisposable。

The total amount of .Net memory in use is the .Net part + all 'external' data in use. 正在使用的.Net内存总量是.Net部分+正在使用的所有“外部”数据。 OS objects, open files, database and network connections all take some resources that are not purely .Net objects. 操作系统对象,打开文件,数据库和网络连接都需要一些非纯粹.Net对象的资源。

Graphics uses Pens and other objects wich are actually OS objects that are 'quite' expensive to keep around. 图形使用笔和其他对象,这些对象实际上是“非常”昂贵的操作系统对象。 (You can swap your Pen for a 1000x1000 bitmap file). (您可以将笔换成1000x1000位图文件)。 These OS objects only get removed from the OS memory once you call a specific cleanup function. 一旦调用特定的清理功能,这些OS对象就会从OS内存中删除。 The Pen and Bitmap Dispose functions do this for you immediatly when you call them. 当您调用它们时,Pen和Bitmap Dispose函数会立即为您执行此操作。

If you don't call Dispose the garbage collector will come to clean them up 'somewhere in the future*'. 如果您不调用Dispose,垃圾收集器将在未来的某个地方进行清理*。 (It will actually call the destructor/finalize code that probably calls Dispose()) (它实际上会调用可能调用Dispose()的析构函数/ finalize代码)

*on a machine with infinite memory (or more then 1GB) somewhere in the future can be very far into the future. *在未来某个地方拥有无限内存(或超过1GB)的机器上可能会非常遥远。 On a machine doing nothing it can be easily longer then 30 minutes to clean up that huge bitmap or very small pen. 在一台什么都不做的机器上,它可以很容易地超过30分钟来清理那个巨大的位图或非常小的笔。

它将保留资源,直到垃圾收集器清理它

Depends if it implements finalizer and it calls the Dispose on its finalize method. 取决于它是否实现了终结器并且在其finalize方法上调用Dispose。 If so, handle will be released at GC. 如果是这样,手柄将在GC发布。

if not, handle will stay around until process is terminated. 如果没有,句柄将保持不变,直到进程终止。

With graphic stuff it can be very bad. 有图形的东西,它可能是非常糟糕的。

Open the Windows Task Manager. 打开Windows任务管理器。 Click "choose columns" and choose column called "GDI Objects". 单击“选择列”并选择名为“GDI对象”的列。

If you don't dispose certain graphic objects, this number will keep raising and raising. 如果您不处理某些图形对象,这个数字将继续升高和提高。

In older versions of Windows this can crash the whole application (limit was 10000 as far as I remember), not sure about Vista/7 though but it's still a bad thing. 在旧版本的Windows中,这可能导致整个应用程序崩溃(据我记得限制为10000),虽然不确定Vista / 7但它仍然是一件坏事。

垃圾收集器无论如何都会收集它但是它很重要:如果你不打电话处理你不使用它的物体,它将在记忆中存活更长时间并被提升到更高代,这意味着收集它的成本更高。

在我的脑海里,第一个想法来到表面的是,一旦方法完成执行,这个对象将被处置!我不知道我在哪里得到这个信息!是不是?

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM