必须在C#中手动清理的资源?

时间:2008-09-22 19:54:04

标签: c# memory-management resources garbage-collection

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

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

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

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

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

14 个答案:

答案 0 :(得分:7)

  • 处理内部窗口数据结构。
  • 数据库连接。
  • 文件句柄。
  • 网络连接。
  • COM / OLE参考。

列表继续。

调用Dispose甚至更好,使用using模式非常重要。

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

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

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

答案 1 :(得分:7)

如果你没有处理某些东西,当垃圾收集器注意到你的代码中没有对它的引用时,它会被清理掉,这可能需要一段时间。对于类似的东西,它并不重要,但对于一个打开的文件,它可能会这样做。

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

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

答案 2 :(得分:3)

不处理您的IDisposables的后果可能会从可忽略的性能影响到崩溃您的应用程序。

示例中的Brush对象将在GC感觉到它时被清理干净。但是你的程序不会受益于你早先清理它所获得的额外内存。如果您使用了大量的Brush对象,这可能会变得很重要。如果GC没有很长时间,那么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)

}

答案 3 :(得分:2)

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

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

答案 4 :(得分:1)

从技术上讲,应该主动处理从IDisposable继承的任何内容。您可以使用'using'语句来简化操作。

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

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

IDisposable的优点在于它使您能够主动发布基础非托管资源。有时您真的想要这样做 - 例如,考虑网络连接和文件资源。

答案 5 :(得分:1)

正如其他人所说,使用是你的朋友。 我写了this blog entry关于如何以一种相当简单的方式实现IDisposable,通过分解最重要的部分来减少错误。

答案 6 :(得分:1)

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

MemoryStream ms = new MemoryStream().Dispose

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

using(MemoryStream ms = new MemoryStream())
{
  ...
}

答案 7 :(得分:0)

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

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

答案 8 :(得分:0)

如果它被管理(即框架的一部分),您不需要担心它。如果它实现了IDisposable,只需将其包装在using块中。

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

this question

下有更多细节

答案 9 :(得分:0)

首先在程序终止时,您可以假设过程本身将消除该过程使用的内存。

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

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

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

答案 10 :(得分:0)

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

与C / C ++相比,C#的一大优势在于您不必关心释放已分配的对象(至少大部分时间);当运行时决定(各种策略何时/如何做)时,gc会这样做。

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

答案 11 :(得分:0)

需要注意的一点是看起来小到GC的对象但不是......例如,在SharePoint API中,就GC而言,SPWeb对象的占用空间很小,所以收集的优先级低,但它确实抓住了GC不知道的一堆内存(我认为在堆中)。如果您要预先处理一大堆这些问题,您将遇到一些有趣的内存问题,请记住使用或处置!

答案 12 :(得分:0)

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

答案 13 :(得分:-3)

垃圾收集器将处理任何托管资源。在您的示例中,当垃圾收集器决定时,刷子将被清除,这将在最后一次引用刷子不再有效后的某个时间发生。

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