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

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

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

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

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

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

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

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

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

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

如果类实现了IDisposable ,则应始终调用Dispose()

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

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

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

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

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

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

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

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

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

笔记:

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

妥善处理所有物体 。 没有理由不去。

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

为了我:

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

Dispose另一个用途是它可以明确地结束组件的生命周期。 比如在C#winform中调用Form.Dispose()将关闭表单。 但是,对于SqlConnection ,人们说只是单独调用Dispose而不显式调用Close将无法保证Close连接。 建议调用CloseDispose 。 我没试过这个。

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

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

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

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

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

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

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

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

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

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

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