11

在C#中必须手动清理哪些资源,不这样做的后果是什么?

例如,假设我有以下代码:

myBrush = new System.Drawing.SolidBrush(System.Drawing.Color.Black);
// Use Brush

如果我不使用 dispose 方法清理画笔,我假设垃圾收集器会释放程序终止时使用的内存?它是否正确?

我还需要手动清理哪些其他资源?

4

14 回答 14

7
  • 对内部 windows 数据结构的句柄。
  • 数据库连接。
  • 文件句柄。
  • 网络连接。
  • COM/OLE 引用。

名单还在继续。

打电话很重要,Dispose甚至更好的是,使用该using模式。

using (SolidBrush myBrush = new System.Drawing.SolidBrush(System.Drawing.Color.Black))
{
    // use myBrush
}

如果你不处理某些东西,当垃圾收集器注意到没有更多对它的引用时,它会被清理掉,这可能是在一段时间之后。

在 的情况下System.Drawing.Brush,Windows 将保持在内存中加载的画笔的内部窗口结构,直到所有程序释放它们的句柄。

于 2008-09-22T19:57:24.763 回答
7

如果您不处置某些东西,那么当垃圾收集器注意到您的代码中不再有对它的引用时,它就会被清理掉,这可能是在一段时间之后。对于这样的事情,这并不重要,但对于打开的文件,它可能确实如此。

一般来说,如果某个东西有一个 Dispose 方法,你应该在完成它后调用它,或者,如果可以的话,将它包装在一个using语句中:

using (SolidBrush myBrush = new System.Drawing.SolidBrush(System.Drawing.Color.Black))
{
    // use myBrush
}
于 2008-09-22T19:59:11.700 回答
3

不处置 IDisposables 的后果可能会从可忽略不计的性能损失到使您的应用程序崩溃。

您的示例中的 Brush 对象将在需要时由 GC 清理。但是,您的程序将无法从早先清理它所获得的那一点额外内存中受益。如果您使用大量 Brush 对象,这可能会变得很重要。如果对象存在时间不长,GC 也更有效地清理对象,因为它是一个分代垃圾收集器。

另一方面,不释放数据库连接对象的后果可能意味着您很快就会耗尽池中的数据库连接并导致您的应用程序崩溃。

要么使用

using (new DisposableThing...
{
    ...
}

或者,如果您需要在对象的生命周期中保留对 IDisposable 的引用,请在您的对象上实现 IDisposable 并调用 IDisposable 的 Dispose 方法。

class MyClass : IDisposable
{
    private IDisposable disposableThing;

    public void DoStuffThatRequiresHavingAReferenceToDisposableThing() { ... }

    public void Dispose()
    {
        Dispose(true);
        GC.SuppressFinalize(this);
    }

    protected virtual void Dispose(bool disposing)
    //etc... (see IDisposable on msdn)

}

于 2008-09-22T20:04:57.677 回答
2

通常,任何实现 IDisposable 的东西都会让您暂停并研究您正在使用的资源。

GC 仅在内存压力时发生,因此您无法预测何时。虽然卸载 AppDomain 肯定会触发它。

于 2008-09-22T19:57:27.260 回答
1

从技术上讲,任何从 IDisposable 继承的东西都应该被主动处置。您可以使用“使用”语句使事情变得更容易。

http://msdn.microsoft.com/en-us/library/yh598w02.aspx

有时您会在文档示例代码以及由工具(即 Visual Studio)生成的代码中看到 IDisposable 派生对象的使用不一致。

IDisposable 的优点在于它使您能够主动释放底层非托管资源。有时您真的很想这样做——例如考虑网络连接和文件资源。

于 2008-09-22T20:01:48.387 回答
1

正如其他人所说,使用是你的朋友。我写了这篇博客文章,介绍如何以一种相当简单的方式实现 IDisposable,通过分解最重要的部分来减少出错的可能性。

于 2008-09-22T20:09:07.060 回答
1

当我不记得给定对象是否是一次性资源时,我使用的一个技巧是在声明后键入“.Dispose”(最多!)让 Intellisense 为我检查:

MemoryStream ms = new MemoryStream().Dispose

然后删除 .Dispose 并使用 using() 指令:

using(MemoryStream ms = new MemoryStream())
{
  ...
}
于 2008-09-22T21:05:52.947 回答
0

好吧,只要你使用资源的托管版本,并且不要自己调用windows API,你应该可以的。当您得到的是 IntPtr 时,只需担心必须删除/销毁资源,因为“Windows 句柄”(以及许多其他东西)在 .NET 中是已知的,而不是对象。

顺便说一句,一旦您离开当前上下文,资源(与任何其他 .NET 对象一样)将被标记为收集,因此如果您在方法中创建 Brush,它会在您退出时被标记。

于 2008-09-22T19:59:10.890 回答
0

如果它是托管的(即框架的一部分),您无需担心它。如果它实现 IDisposable 只需将其包装在一个using块中。

如果您想使用非托管资源,那么您需要阅读终结器并自己实现 IDisposable。

这个问题下有更多细节

于 2008-09-22T20:00:18.920 回答
0

首先在程序终止时,您可以假设进程使用的内存将随进程本身一起被消除。

在.net中使用dispose或析构函数时,必须明白GC调用dispose函数的时间是不确定的。这就是为什么建议显式使用 using 或调用 dispose 的原因。

使用文件等资源时,必须释放内存对象(如信号量)和 .net 托管世界之外的资源。

例如,您需要处理 SolidBrush,因为它是一个 GDI 对象并且存在于 .net 世界之外。

于 2008-09-22T20:03:44.077 回答
0

垃圾收集器不仅在程序终止时释放,否则它不会真正有用(在任何体面/最近的操作系统上,当进程退出时,它的所有内存都会被操作系统自动清理)。

与 C/C++ 相比,C# 的一大优势是您不必关心释放分配的对象(至少在大多数情况下);gc 在运行时决定时执行它(各种策略何时/如何执行)。

gc 不处理许多资源:文件、与线程相关的资源(锁)、网络连接等......

于 2008-09-22T20:04:05.073 回答
0

需要注意的一个地方是对 GC 来说看起来很小但实际上并不大的对象......例如,在 SharePoint API 中,就 GC 而言,SPWeb 对象占用的空间很小,因此收集的优先级较低,但它确实抓住了GC不知道的一堆内存(我相信在堆中)。例如,如果您正在学习一大堆这些,您会遇到一些有趣的记忆问题,请始终记住使用 using 或 dispose!

于 2008-09-22T20:08:40.727 回答
0

与其将对象视为“持有”需要释放的资源,不如将对象视为更改了某些内容(可能在计算机之外!),这将比它更长寿,如果它在某种程度上可能是有害的没有撤消或“清理”,但只有对象才能清理。虽然这种更改通常采用标记为“忙碌”的池中的某个具体对象的形式,但其精确形式并不重要。重要的是需要撤消更改,并且对象包含执行此操作所需的信息。

于 2010-12-01T23:10:38.590 回答
-3

垃圾收集器将处理任何托管资源。在您的示例中,当垃圾收集器决定清理画笔时,这将在最后一次对画笔的引用不再有效后的一段时间内发生。

有些事情需要手动清理,但那些是从非托管源检索的指针,例如 DLL 调用,但是 .NET Framework 中的任何内容都不需要这种处理。

于 2008-09-22T19:58:40.033 回答