为什么要调用Dispose()?内存泄漏不会发生?

时间:2013-04-10 01:55:56

标签: c# memory-management memory-leaks idisposable

编辑:我的问题是没有得到我正在寻找的主要答案。我不清楚。我真的想知道两件事:

  1. 不能调用Dispose()导致内存泄漏吗?
  2. 如果你有一个大型程序并且从不在任何IDisposable对象上调用Dispose(),那么最糟糕的事情是什么?
  3. 我的印象是,如果Dispose()对象未调用IDisposable,则可能会发生内存泄漏。

    根据对此thread的讨论,我的看法不正确;如果未调用Dispose(),则不会发生内存泄漏。

    为什么还要打电话给Dispose()呢?是否只是立即释放资源,而不是以后的某个时间?如果你有一个大型程序而且从未在任何Dispose()个对象上调用IDisposable,那么最糟糕的事情是什么?

7 个答案:

答案 0 :(得分:10)

Dispose用于释放非托管资源。这可能意味着内存,如果一个类分配了非托管内存,但它通常是本机对象和资源,如打开文件和数据库连接。

您经常需要在一个本身没有任何非托管资源的类上调用Dispose,但它确实包含另一个可以一次性使用且可能具有非托管资源的类。

开发人员有时也可以使用dispose来确保确定性的最终确定 - 保证资源的释放顺序。

另请注意,如果未调用Dispose,则实现dispose的类通常还会有一个终结器来释放resourcdes。具有终结器的对象具有与没有终结器的类不同的生命周期。当他们准备好GC时,GC会看到他们有一个终结器,而不是在GC准备就绪时立即收集对象,它将它放入终结队列。这意味着该对象可用于一次额外的GC迭代。当你调用dispose时,实现通常(但不是必须)调用GC.SuppressFinalize(),这意味着不再需要调用终结器。

如果某个类实现IDisposable,则应始终致电Dispose()

答案 1 :(得分:7)

虽然其他一些答案似乎暗示你可以逃避而不是打电话,这是非常糟糕的建议。您应始终在任何Dispose资源上调用IDisposable

某些.NET对象具有所谓的“终结器” - 您可以在自己的类上定义,但很少在典型的C#程序员代码中看到它们。终结器是垃圾收集器销毁对象时运行的,有时它会调用Dispose - 但只有当类的实现者这样做时才会运行。

最佳做法是始终Dispose - 无论如何。我使用了很多库,而不是在资源上调用Dispose导致内存泄漏,连接泄漏,操作系统资源泄漏或其他类型的可怕。垃圾收集器不会协调问题,因为它们没有实现任何自定义终结器。

参见相关内容:Will the Garbage Collector call IDisposable.Dispose for me?

答案 2 :(得分:3)

约定是如果一个对象实现了IDisposable,你应该调用Dispose()或使用“using”模式。 Dispose()和等待析构函数(终结器)之间的区别在于Dispose()被立即调用,可用于释放一些重要的资源,如数据库连接,文件,设备,非托管的项目等。

总结一下 - 如果它是IDisposable - Dispose()它!

答案 3 :(得分:3)

不会调用Dispose(*参见关于错误实现的注释2)会导致传统的"memory leak"(内存永远不会被释放,直到进程结束)。

与记忆相关的“唯一”事情是它将在未来的非确定性时刻被释放。

Dispose个对象的一个​​有趣的例子是非常小的托管对象拥有大量非托管内存(即分配了一些Win32内存管理函数,即HeapAlloc)。在这种情况下,托管内存管理器可能无法正确检测内存压力以触发Gen2 GC(特别是在x86 - 32位进程的情况下),它可能过早地无法为您的进程分配托管内存。在这种情况下的另一个问题是地址空间碎片“等待GC被解除分配”(再次主要在x86情况下) - 当分配较小的本机内存块时,它们之间有一些相对较大的空间,防止分配大块管理内存管理所需。

注意:

  1. 这个答案通过不处理IDisposable对象管理内存来明确地讨论true memory leaks /内存分配问题。虽然这种做法确实没有“真正的内存泄漏”,但大多数人会考虑将内存使用量增加为内存泄漏(类似于在应用程序的生命周期内将大量对象存储在静态列表/字典中)。
  2. 可以创建管理本机内存的对象,并错误地实现IDisposable模式。在这种情况下,可能会真正泄漏本机内存(无论是否调用Dispose)。
  3. 在大多数情况下,实现IDisposable的对象根本不管理内存。对于大多数实用的C#程序,由这些对象管理的本机资源是系统资源的句柄,如文件,位图,字体,同步对象或COM本机对象。不及时处理它们会导致其他问题。
  4. 正确处理所有对象。没有理由不去。

答案 4 :(得分:2)

Dispose()旨在释放垃圾收集器不会释放的资源,例如数据库连接。这些资源也应该在终结器中释放,但是终结器比Dispose()方法慢得多。

答案 5 :(得分:2)

对我来说:

Dispose可用于using()范围。这可以帮助我确定IDisposeable组件的生命周期。我通常在StreamWriter / Reader或SqlConnection类中使用它。

Dispose的另一个用途是它可以明确地结束组件的生命周期。比如在C#winform中调用Form.Dispose()将关闭表单。但是,对于SqlConnection,人们说仅在不明确调用Dispose的情况下单独调用Close并不能保证关闭连接。建议同时致电CloseDispose。我没有试过这个。

另一件事,在调用Dispose()之后,GC可以释放内存,因为他们知道对象的生命周期已经结束,而不是等待生命结束。

类似的问题可能是C# disposing IDisposable

答案 6 :(得分:1)

  

不能调用Dispose()导致内存泄漏吗?

是的,当然。以下只是一个例子。

假设您的应用程序中有一个主窗口,并且您创建了一个具有主窗口事件订阅的子控件。您在Dispose上取消订阅它们。如果您没有处置,主窗口可以保留对您的子控件的引用,直到您关闭该应用程序。

  

如果你有一个大型计划,那么最糟糕的事情是什么?   永远不要在任何IDisposable对象上调用Dispose()?

最糟糕的情况是在关闭应用程序之前不会释放一些不需要的内存。

另一个问题是,如果您在需要时从未实施IDisposable或finalization,该怎么办?

发生内存泄漏的最坏情况是保留该内存,直到重新启动PC为止。只有当您拥有未经管理的资源并且您没有实施dispose / finalize时,才会发生这种情况。如果您实现Idisposable接口并实现终结器,则终结过程将为您执行Dispose。

你应该调用Dispose的另一个原因是抑制终结。

如前所述,如果有任何使用Finalize方法的对象并且您没有调用Dispose。该对象可以驻留在内存中两个GC周期。在第一个循环中,它将该实例排入最终化队列,并在GC过程之后进行最终化。因此,只有下一个GC周期才能释放该内存。