12

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

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

Dispose()我的印象是,如果不在对象上调用,可能会发生内存泄漏IDisposable

根据关于这个线程的讨论,我的看法是不正确的;Dispose()如果不调用,则不会发生内存泄漏。

那为什么还要麻烦打电话Dispose()呢?是否只是立即释放资源,而不是稍后的某个时间?如果您有一个大型程序并且从不调用Dispose()您的任何IDisposable对象,那么可能发生的最糟糕的事情是什么?

4

7 回答 7

11

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

您经常需要对本身没有任何非托管资源的类调用 Dispose,但它确实包含另一个可丢弃的类并且可能具有非托管资源。

有时对开发人员来说,实现 dispose 以确保确定性最终确定也很有用——保证资源被释放的顺序。

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

如果一个类实现IDisposable,你应该总是调用Dispose().

于 2013-04-10T02:00:01.223 回答
7

虽然其他一些答案似乎暗示您可以不调用它而侥幸逃脱,但这确实是个糟糕的建议。您应该始终调用Dispose任何IDisposable资源。

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

最好的做法是永远Dispose——无论如何。我使用过很多库,其中不调用Dispose资源会导致内存泄漏、连接泄漏、操作系统资源泄漏或其他类型的可怕。垃圾收集器不会解决问题,因为它们没有实现任何自定义终结器。

请参阅相关:垃圾收集器会为我调用 IDisposable.Dispose 吗?

于 2013-04-10T02:07:46.653 回答
3

不调用 Dispose 永远不会(*参见关于错误实现的注释 2)导致传统的“内存泄漏”(内存在进程结束之前永远不会被释放)。

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

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

笔记:

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

妥善处理所有物品。没有理由不这样做。

于 2013-04-10T04:27:58.837 回答
3

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

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

于 2013-04-10T02:03:09.117 回答
2

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

于 2013-04-10T01:58:46.043 回答
2

为了我:

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

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

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

类似的问题可能是C# 处理 IDisposable

于 2013-04-10T02:02:49.267 回答
1

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

是的当然。下面只是一个例子。

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

如果您有一个大型程序并且从不对任何 IDisposable 对象调用 Dispose(),那么可能发生的最糟糕的事情是什么?

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

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

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

您应该调用 Dispose 的另一个原因是禁止完成。

正如我之前指出的,如果有任何对象具有 Finalize 方法并且您没有调用 Dispose。该对象可以在内存中驻留两个 GC 周期。在第一个周期中,它将该实例排入终结队列,并在 GC 过程之后发生终结。因此,只有下一个 GC 周期才能释放该内存。

于 2016-02-09T05:19:33.760 回答