【问题标题】:Memory Leaks C# [closed]内存泄漏 C# [关闭]
【发布时间】:2011-06-28 14:15:59
【问题描述】:

我试图更好地理解内存泄漏的概念。谁能指出一些有用的信息,这些信息可以帮助我更好地理解内存泄漏是什么以及如何在我的代码中找到它们。

【问题讨论】:

  • 这是我不久前在 .Net 内存泄漏上写的一篇博文:crazorsharp.blogspot.com/2009/03/…
  • Windows 注册表 → 搜索 → “HeapLeakDetection”
  • 这到底是怎么回事?他想知道内存泄漏,这是直接可以回答和直接相关的,在我的谦虚但正确的意见中。
  • 这里有点晚了.. 我同意这在我看来也不是题外话
  • 有人投票删除这个问题让我很担心。好的,这可能偏离主题有几个原因,但删除它是个坏主意。它包含有用的信息

标签: c# memory-leaks


【解决方案1】:

当您的程序动态分配的内存在您完成使用后没有正确释放时,就会发生内存泄漏。如果您有一个程序不断执行此操作,您的泄漏会越来越大,很快您的程序就会占用您所有的 RAM。

【讨论】:

    【解决方案2】:

    一个很好的阅读是Everybody thinks about garbage collection the wrong way

    一般来说,内存泄漏或任何资源泄漏是指程序分配内存(或任何其他资源),然后在完成时忽略释放它。在本机应用程序中,内存泄漏是最常见的资源泄漏,当资源引用(指向已分配块的指针)超出范围并被销毁,但分配的资源(内存块)没有被销毁时,可能会发生这种情况。在这种情况下,资源(内存)泄漏是因为程序失去了释放它的能力,即使它想释放它,因为它不再记住资源的位置(块的地址)。

    在托管应用程序中,内存泄漏有点棘手。由于运行时可以自动跟踪对资源的引用,它还可以了解应用程序的任何活动部分何时不再引用资源(对象)(在任何线程上都没有从堆栈帧到该资源的引用链)因此运行时可以了解何时可以安全地收集应用程序不再引用的对象。因此,在托管世界中,当您认为应用程序不再引用某个对象(因此它可以被运行时收集)但实际上,通过一些引用链,您确实时会发生“泄漏” > 对其有参考,因此无法收集。

    我强烈推荐上面链接的Raymond Chen的文章,非常有启发性。

    【讨论】:

    【解决方案3】:

    当您分配内存,然后以某种方式“忘记”返回或释放它时,就会发生传统的内存泄漏。在旧的 C++ 代码中,这意味着调用 new 而没有对应的 delete。在 C 中,这意味着对 alloc()/malloc() 的调用没有对应的 free()

    在 .Net 中,您不会遇到传统意义上的内存泄漏,因为您不应该自己释放内存。没有您需要使用的 free()delete 等价物。甚至 IDisposable 和终结器也与内存无关。相反,您依靠垃圾收集器 (GC) 为您释放内存。

    但是,这并不意味着您永远不会忘记记忆。有几种方法你可能会不小心保留一个引用,从而阻止垃圾收集器完成它的工作。其中包括全局变量(尤其是列表、字典和其他可能用于“缓存”对象的集合类型)、挂在对象引用上的事件处理程序、递归历史引用和大型对象堆。

    在此还需要注意的是,.Net 中内存使用量增加的模式并不一定意味着您的应用正在泄漏内存。在整体内存压力较低的情况下,垃圾收集器可能会选择不收集来节省时间,或者只在进程的现有地址空间内收集而不将内存返回给操作系统。

    【讨论】:

    • “老 C++”会不会是没有智能指针的 C++?
    【解决方案4】:

    内存泄漏有很多种,但一般而言,该术语指的是某种不再使用但仍占用内存的资源。如果您有很多这样的应用程序,那么您的应用程序会占用大量内存,最终您会用完它。

    在 C# 中,这些是一些常见的内存泄漏:

    • 不删除事件侦听器。使用匿名方法或引用外部对象的 lambda 表达式创建的任何事件侦听器都将使这些对象保持活动状态。请记住在不再使用事件侦听器时将其移除。
    • 在不使用数据库连接或结果集时保持打开状态。请记住在所有 IDisposable 对象上调用 Dispose()Use the using statement
    • 使用 p/Invoke 调用 C 函数,该函数分配内存,然后您永远不会释放。

    【讨论】:

      【解决方案5】:

      “内存泄漏”应定义为“当您认为不应使用它时使用的内存”,以应用于垃圾收集语言/运行时如 C#/Java。

      传统上,“内存泄漏”被定义为未正确释放的内存(请参阅其他答案中的维基百科链接),垃圾收集环境通常不会发生这种情况。请注意,由于运行时的问题,即使是垃圾收集语言也可能会泄漏内存 - 即,即使 JavaScript 是垃圾收集语言,在 Internet Explorer 的 JavaScript 运行时中也很容易泄漏大量 JavaScript 对象。

      【讨论】:

        【解决方案6】:

        当内存分配给应用程序时,应用程序有义务将该内存释放回操作系统,以便其他应用程序可以重新使用它。当应用程序不释放该内存从而阻止它被重新分配时,就会发生内存泄漏。

        对于托管代码,垃圾收集器跟踪对应用程序创建的对象的引用。在大多数情况下,CLR 将代表正在运行的进程以合理的方式透明地处理内存分配和释放。然而,.NET 开发人员仍然需要考虑资源管理,因为尽管垃圾收集器的工作仍然存在内存泄漏的情况。

        考虑以下代码:

        Widget widget = new Widget();

        上面的代码行创建了一个 Widget 类的新实例,并且为 widget 字段分配了对该对象的引用。 GC 跟踪与每个对象关联的引用,并释放没有强引用的对象的内存。

        值得一提的是,CLR 的垃圾收集只会收集托管对象,.NET 代码可以并且确实经常使用无法自动进行垃圾收集的非托管资源。

        当分配了这些资源的对象在对这些资源的最后一次引用超出范围之前未能正确解除分配时,就会发生非托管资源泄漏,这会留下已分配但未引用的资源,因此对应用程序不可用。

        直接引用非托管资源的类应确保正确释放这些资源。执行此操作的示例如下所示:

        public void ManagedObject : IDisposable
        {
            //A handle to some native resource.
            int* handle;
        
            public ManagedObject()
            {
                //AllocateHandle is a native method called via P/Invoke.
                handle = AllocateHandle();
            }
        
            public void Dispose()
            {
                Dispose(true);
                GC.SuppressFinalize(this);
            }
        
            private void Dispose(bool disposing)
            {
                if (disposing)
                {
                    //deal with managed resources here
                    FreeHandle(handle);
                }
            }
        
            ~ManagedType()
            {
                Dispose(false);
            }
        }
        

        disposing 参数在从终结器调用时为 false。这是为了防止在终结器中使用托管资源,因为托管引用在该阶段应被视为无效。

        还要注意Dispose() 方法调用GC.SuppressFinalize(this) 这会阻止终结器为该实例运行。这样做是因为本来在终结器中释放的资源在 Dispose 调用中被释放,因此不需要终结器调用。

        使用处理非托管资源的类(或任何实现 IDisposable 的类)的客户端代码应在 using 块中执行此操作,以确保在不再需要访问资源时调用 IDisposable.Dispose这将处理托管和非托管资源,并且在上面的示例中,确保不会对终结器进行非常昂贵的调用。

        感谢我的漫无边际。我现在就停下来。

        【讨论】:

          猜你喜欢
          • 2017-02-19
          • 2013-03-08
          • 2013-03-29
          • 2010-11-28
          • 2018-08-26
          • 2013-07-12
          • 2012-09-10
          • 1970-01-01
          • 2019-09-25
          相关资源
          最近更新 更多