【问题标题】:When should I dispose my objects in .NET?我应该什么时候在 .NET 中处理我的对象?
【发布时间】:2010-09-19 17:44:33
【问题描述】:

对于一般代码,我真的需要处置一个对象吗?我可以在大多数情况下忽略它,还是当你 100% 确定不再需要它时总是处理它是个好主意?

【问题讨论】:

    标签: .net garbage-collection dispose


    【解决方案1】:

    用完后立即将其丢弃。一次性对象表示持有 CLR 本质上不知道的有价值资源的对象。因此,GC 也不知道资源,也无法就何时应该收集一次性对象并因此释放底层资源做出明智的决定。

    最终 GC 会感到内存压力并巧合地收集您的对象(仅此而已)。如果您不以确定的方式处理对象,那么完全有可能进入几乎没有内存压力的资源匮乏状态。

    这是如何发生的快速示例。让我们将底层资源视为 Win32 句柄。这些是非常有限且相当小的。您运行一个创建大量 Foo 对象的操作。 Foo 对象实现 IDisposable 并负责创建和处置 Win32 句柄。它们不是手动释放的,而是通过不同的怪癖使其进入 Gen2 堆。这个堆很少被释放。随着时间的推移,足够多的 Foo 实例进入 Gen2 堆以占用所有可用句柄。因此,无论使用多少内存,都无法创建新的 Foo 对象。

    事实上,要释放句柄,需要在单个操作期间分配相当多的内存才能提供足够的压力来释放实例。

    【讨论】:

    • System.Drawing 类没有正确通知 GC 内存压力,因此它们没有按应有的优先级处理(它将 80mb 对象视为 1k 或更少)
    【解决方案2】:

    如果对象实现了 IDisposable,则应在完成后立即将其丢弃。最简单的方法是用using 块包围它:

    using (SqlCommand cmd = new SqlCommand(conn)) {
        cmd.ExecuteNonQuery();
    }
    

    【讨论】:

      【解决方案3】:

      对于实现IDisposable 的任何类型,您应该始终调用Dispose() 的原因是它通常用于表示该类型获取非托管资源。尤其重要的是要尽早释放它们。正如其他人所提到的,using 是执行此操作的首选方式。

      【讨论】:

        【解决方案4】:

        有几种方法可以查看它。一种方法试图确定是否真的有必要在不再需要对象时立即处理它,例如使用 Reflector 查看它是否真的持有非托管资源,或者它们是否是偶然的无论如何处置。另一种观点是假设如果一个对象实现了 IDisposable,那么确定是否真的需要调用 Dispose() 不是你的事——你总是调用它。我认为这是正确的方法。窥视对象的私有实现以决定如何使用它们会增加与可能更改的实现耦合的风险。一个例子是 LINQ to SQL DataContext。它实现了 IDispose,但主要是在不需要显式调用 Dispose() 后自行清理。我的偏好是编写显式处理的代码,但其他人认为没有必要。

        当然,这一切都适用于实现 IDisposable 的对象。确实,GC 会在您没有任何明确操作的情况下处理大多数其他事情,但是值得阅读一下关于 GC 行为的微妙之处(我现在太累了,无法考虑细节)以了解何时显式地处理对象,更重要的是,何时实现 IDispose。网上有很多关于这件事的好文章。

        如前所述, using(..) { ... } 是 IDisposable 实现者的朋友。

        【讨论】:

          【解决方案5】:

          如果对象实现了 IDisposable,它很可能会占用非托管资源。因此,经验法则是在您完成对象的那一刻调用 Dispose,直接或通过 using 块。不要依赖 GC,因为这就是 IDisposable 的用途 - 资源的确定性释放。

          【讨论】:

            【解决方案6】:

            不,在您没有持有非托管资源的情况下,您可以调用 Dispose。但是,如果您的班级持有非托管资源,例如需要删除的临时文件,那么您将不得不显式调用 Dispose。

            您可以通过在 Finalize 方法中编写释放代码来避免调用 Dispose,但是您依赖于垃圾收集器,因为您永远无法确定垃圾收集器何时会完成您的对象。为了安全起见,如果您正在设计这样一个包含非托管资源的类,您可以在 Dispose 和 Finalize 方法中编写相同的对象释放代码,但如果这样做,请始终在您的 dispose 方法中使用 SuppressFinalize()因为如果您的对象已经在终结队列中,它将阻止调用 Finalize() 方法。

            【讨论】:

            【解决方案7】:

            在大多数情况下,依靠 GC“有效”。典型的例外是当您有大量资源交互时 - 在这种情况下最好显式地处置。

            显而易见的例子。

            using (var conn = new SqlConnection(connString)) {}
            

            “使用”块绝对是确保正确处理对象的最干净、最可靠的方法。 'Using' 块可以与任何实现 IDisposable 的对象一起使用。

            【讨论】:

            • GC 自己运行纯属巧合。没有义务及时调用 Dispose。
            • 我没有建议 GC 有义务及时这样做。我坚持我的说法,即不需要手动处理常规物品。
            • 那么,例如,当你创建一个Bitmap实例时,你不认为你需要手动dispose它吗?再想一想。 GC 将 30MB 对象视为 10 个字节并无限期地忽略它。它每次都会使您的服务器崩溃。
            • WPF 对未损坏的对象使用人工 GC 压力估计来避免这种情况,但还有其他错误使其在服务器上不受支持。
            • 虽然就个人而言,我通常会在完成后尝试处理(就像 berko 所说,使用块......),这个答案没有任何问题,因为他在非资源密集型中是对的情况(您可以将 IDisposable 用于不安全数据以外的情况)通常不太重要。我认为他的意思是这是一个判断电话。在大多数情况下,包装在“使用”中将是正确的调用,但是如果它被传递到这个地方(尤其是不同的线程)呢? @Computer Linguist - 在“让你的服务器 EST 崩溃”之类的 cmets 的回答令人发炎后 3 年后出现
            【解决方案8】:

            当你完成一个对象时,你可以忘记它。只要它没有在任何地方被引用,那么它就和消失一样好。当垃圾收集器喜欢它时,它使用的内存就会被释放。

            【讨论】:

            • 有人能解释一下为什么这个答案会被这样抨击吗? -6?没有意见?
            • @bobobobo 我认为是因为它没有解决 IDisposable 问题,它旨在允许对象在不再需要时释放 .NET 运行时外部的资源,而不是等待 GC。
            猜你喜欢
            • 2023-04-10
            • 2010-09-28
            • 1970-01-01
            • 1970-01-01
            • 2010-12-30
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多