【问题标题】:Resources that have to be manually cleaned up in C#?必须在 C# 中手动清理的资源?
【发布时间】:2010-09-12 03:23:16
【问题描述】:

C#中哪些资源需要手动清理,不这样做会有什么后果?

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

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

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

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

【问题讨论】:

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


    【解决方案1】:

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

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

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

    【讨论】:

      【解决方案2】:
      • 内部窗口数据结构的句柄。
      • 数据库连接。
      • 文件句柄。
      • 网络连接。
      • COM/OLE 引用。

      名单还在继续。

      调用Dispose 很重要,或者更好的是,使用using 模式。

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

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

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

      【讨论】:

        【解决方案3】:

        不释放 IDisposable 的后果可能会有所不同,从可忽略不计的性能影响到导致应用崩溃。

        您的示例中的 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)
        

        }

        【讨论】:

          【解决方案4】:

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

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

          【讨论】:

            【解决方案5】:

            从技术上讲,应主动处置从 IDisposable 继承的任何内容。您可以使用 'using' 语句让事情变得更简单。

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

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

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

            【讨论】:

              【解决方案6】:

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

              【讨论】:

                【解决方案7】:

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

                MemoryStream ms = new MemoryStream().Dispose
                

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

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

                【讨论】:

                • 请注意,如果 Dispose 已明确实施,此技巧将不起作用!
                【解决方案8】:

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

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

                【讨论】:

                  【解决方案9】:

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

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

                  this question下面有很多详细信息

                  【讨论】:

                    【解决方案10】:

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

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

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

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

                    【讨论】:

                      【解决方案11】:

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

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

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

                      【讨论】:

                        【解决方案12】:

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

                        【讨论】:

                          【解决方案13】:

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

                          【讨论】:

                            【解决方案14】:

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

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

                            【讨论】:

                            • 这个答案是为了解释你什么时候需要清理你的资源,而不是你应该什么时候,就像其他人那样。从最初问题的语气来看,我认为这就是我们想要的。
                            猜你喜欢
                            • 2023-03-14
                            • 2011-03-06
                            • 1970-01-01
                            • 1970-01-01
                            • 1970-01-01
                            • 1970-01-01
                            • 1970-01-01
                            • 2012-08-27
                            • 1970-01-01
                            相关资源
                            最近更新 更多