编辑:我的问题是没有得到我正在寻找的主要答案。我不清楚。我真的想知道两件事:
Dispose()
导致内存泄漏吗?我的印象是,如果Dispose()
对象未调用IDisposable
,则可能会发生内存泄漏。
根据对此thread的讨论,我的看法不正确;如果未调用Dispose()
,则不会发生内存泄漏。
为什么还要打电话给Dispose()
呢?是否只是立即释放资源,而不是以后的某个时间?如果你有一个大型程序而且从未在任何Dispose()
个对象上调用IDisposable
,那么最糟糕的事情是什么?
答案 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情况下) - 当分配较小的本机内存块时,它们之间有一些相对较大的空间,防止分配大块管理内存管理所需。
注意:
IDisposable
对象管理内存来明确地讨论true memory leaks /内存分配问题。虽然这种做法确实没有“真正的内存泄漏”,但大多数人会考虑将内存使用量增加为内存泄漏(类似于在应用程序的生命周期内将大量对象存储在静态列表/字典中)。 IDisposable
模式。在这种情况下,可能会真正泄漏本机内存(无论是否调用Dispose
)。 IDisposable
的对象根本不管理内存。对于大多数实用的C#程序,由这些对象管理的本机资源是系统资源的句柄,如文件,位图,字体,同步对象或COM本机对象。不及时处理它们会导致其他问题。正确处理所有对象。没有理由不去。
答案 4 :(得分:2)
Dispose()
旨在释放垃圾收集器不会释放的资源,例如数据库连接。这些资源也应该在终结器中释放,但是终结器比Dispose()
方法慢得多。
答案 5 :(得分:2)
对我来说:
Dispose
可用于using()范围。这可以帮助我确定IDisposeable
组件的生命周期。我通常在StreamWriter
/ Reader或SqlConnection
类中使用它。
Dispose
的另一个用途是它可以明确地结束组件的生命周期。比如在C#winform中调用Form.Dispose()
将关闭表单。但是,对于SqlConnection
,人们说仅在不明确调用Dispose
的情况下单独调用Close
并不能保证关闭连接。建议同时致电Close
和Dispose
。我没有试过这个。
另一件事,在调用Dispose()
之后,GC可以释放内存,因为他们知道对象的生命周期已经结束,而不是等待生命结束。
类似的问题可能是C# disposing IDisposable
答案 6 :(得分:1)
不能调用Dispose()导致内存泄漏吗?
是的,当然。以下只是一个例子。
假设您的应用程序中有一个主窗口,并且您创建了一个具有主窗口事件订阅的子控件。您在Dispose上取消订阅它们。如果您没有处置,主窗口可以保留对您的子控件的引用,直到您关闭该应用程序。
如果你有一个大型计划,那么最糟糕的事情是什么? 永远不要在任何IDisposable对象上调用Dispose()?
最糟糕的情况是在关闭应用程序之前不会释放一些不需要的内存。
另一个问题是,如果您在需要时从未实施IDisposable或finalization,该怎么办?
发生内存泄漏的最坏情况是保留该内存,直到重新启动PC为止。只有当您拥有未经管理的资源并且您没有实施dispose / finalize时,才会发生这种情况。如果您实现Idisposable接口并实现终结器,则终结过程将为您执行Dispose。
你应该调用Dispose的另一个原因是抑制终结。
如前所述,如果有任何使用Finalize方法的对象并且您没有调用Dispose。该对象可以驻留在内存中两个GC周期。在第一个循环中,它将该实例排入最终化队列,并在GC过程之后进行最终化。因此,只有下一个GC周期才能释放该内存。