【问题标题】:How to avoid Memory Leaks? [closed]如何避免内存泄漏? [关闭]
【发布时间】:2009-04-27 17:25:23
【问题描述】:

我可以使用哪些技巧来避免应用程序中的内存泄漏?有什么我需要注意的问题或陷阱吗?

【问题讨论】:

  • 唯一可靠的方法是不分配任何内存。 ;-)
  • 这个问题需要更多信息,即,您试图避免内存泄漏的语言和上下文是什么?
  • @Rob: 语言是 c#,每个标签
  • @Jeff - 它被否决了,因为它太宽泛了。它是一个 WinForms 应用程序吗? ASP.NET 应用程序?在这个问题上投入一些时间和精力,它会赢得支持。
  • 我不认为这真的很重要,约翰。我认为它足够窄。

标签: c# memory-leaks


【解决方案1】:

对 IDisposable 对象调用 Dispose 或使用 using 子句。这应该可以解决我能想到的大部分泄漏问题。

【讨论】:

  • 不信任所有非托管且未包装在适当 IDisposable 对象中的代码。
【解决方案2】:

注意删除您使用的任何事件处理程序。在 .NET 中,它们是导致内存泄漏的最常见原因。

【讨论】:

【解决方案3】:

正如 ocdecio 所提到的,请务必在 Idisposable 对象上调用 Dispose,并记住在处理完对象后删除事件处理程序。在构建使用非托管资源的类时,请务必实现 Idisposable,这样用户就会知道有需要处置的关键资源。

此外,即使垃圾收集为您做了很多工作,您也应该摆脱对已完成对象的引用。否则它们仍然有根,不会被 GC 处理。

【讨论】:

    【解决方案4】:

    不要低估工具在这些情况下的作用。 .NET 内存分析器现在已经相当成熟和强大,如果您有一个复杂的应用程序,其中您认为应该释放的对象仍然被其他东西作为引用保存,那么查明该引用的能力是非常宝贵的。

    我刚刚解决了 WPF 选项卡页托管 Windows 窗体控件的内存泄漏问题(因为这些选项卡包含大量数据,您可以随意打开和关闭它们,只需等待 GC 清除内存关闭不是一种选择)。我使用YourKit 分析器在打开选项卡之前拍摄内存快照,打开选项卡,再次关闭它并拍摄另一个快照。在分析器中,您可以直观地比较两个快照,并查看哪些对象在该过程中幸存下来,并根据它们的引用返回 GC 根。我没有使用其他分析器的经验,但我知道如果 YourKit 不能满足您的需求,会有一些分析器。


    编辑添加:

    好的,这不是避免内存泄漏,而是修复它们。但我将把它留在这里,因为我认为这是有用的信息,而且我认为没有足够多的 .NET 开发人员了解这些工具。

    【讨论】:

      【解决方案5】:

      我知道有些人会建议将垃圾收集作为解决方案。但是在很多情况下,垃圾收集并没有给您预期的结果。最终很容易坚持一个杂散的引用,这会阻止整个内存链被释放。 Read 关于这如何破坏了 DARPA 大挑战的条目。您可以争辩说这些不是内存泄漏,但如果程序希望释放内存,它仍然是一个问题。就像在 C 编程中一样,几个月后,您就会掌握如何确保不会留下不需要的引用。

      【讨论】:

        【解决方案6】:

        内存泄漏是错误,所以一般来说 - 这个问题可以像“如何在没有错误的情况下编写代码”一样回答?从长远来看 - 不可能没有错误,但您可以限制在发布的代码中出现错误的机会。

        从关心开发的代码质量开始,并遵循其他人提到的准则。

        1. 简单就是金 - 代码越简单 - 出现错误或泄漏的机会就越少
        2. 使用非托管资源时要小心 - Windows 句柄、数据库连接、GDI 对象等。
        3. 用于 IDisposables
        4. 为携带非托管资源的类实现 IDisposable
        5. 确保删除对未使用对象的所有引用 - 包括棘手的事件处理程序

        除此之外 - 实施测试以查看内存是否泄漏 - 单元、并发、压力和负载测试在这方面最有帮助。通过检查指标(性能计数器)查看内存是否泄漏。您还可以记录对象的创建和销毁,并在测试结束时分析日志。

        【讨论】:

          【解决方案7】:

          我遇到了对象 (Ping) 通过实现 IDisposable 接口并同时继承它两次实现 Dispose() 的问题。继承的方法什么也没做,因此在调用 Dispose() 时必须将对象强制转换为 IDisposable,否则会泄漏内存。这是post I wrote a few years ago 的详细信息。

          【讨论】:

            【解决方案8】:
            1. 将任何一次性的东西包装在 using 构造中。
            2. 避免使用 COM 对象。
            3. 检查所有事件处理程序是否已正确删除。
            4. 检查所有数据绑定是否已正确删除。
            5. 保持简单

            如果您的应用程序逻辑变得不必要地复杂,您可能会开始出现内存泄漏。如果你保持你的类很小并遵循一般的编码实践,你可能不会在托管代码中遇到任何内存泄漏。这是可能的,但不像过去那样可能。

            如果您怀疑存在内存泄漏,请使用分析器查看是否有任何对象被保留的时间超过所需时间。

            我上一次遇到严重的内存泄漏是 .NET 1.1,结果发现框架中存在错误。

            【讨论】:

              【解决方案9】:

              garbage collector 的工作原理有基本的了解将帮助您避免滥用内存。例如,如果您保留对不再需要的对象的引用,gc 将无法收集它。

              同样,如果您要存储用户输入的数据或随时间添加的数据,则应考虑某种限制,以免内存使用量无限增长。

              【讨论】:

                【解决方案10】:

                我在 .NET 中遇到的大多数内存泄漏都与使用 COM 对象和未正确释放它们有关。当我看到对 COM 对象的引用时,我会认为是“内存泄漏”。

                【讨论】:

                  【解决方案11】:

                  内存没有被 GC 销毁的最常见情况是当您的事件处理程序没有正确解除挂钩时。如果需要,您可以在 Dispose() 中取消挂钩。

                  我更详细地描述了问题,并且我有办法编写测试以确定您是否泄漏对象here

                  【讨论】:

                    【解决方案12】:

                    正如其他人所说,调用 Dispose()(或使用 using 语句),但另外考虑类是否使用资源,如果是,则实现 IDisposeable。我的代码中最常见的问题是我有一个类,其成员在 GC 之前不会被清理。

                    【讨论】:

                      【解决方案13】:

                      它是托管代码,即 c#,所以你必须努力泄漏内存:P

                      试试谷歌: http://www.google.com/search?hl=en&client=firefox-a&rls=org.mozilla%3Aen-US%3Aofficial&hs=Mbp&q=c%23+memory+leaks&btnG=Search

                      【讨论】:

                      • 这并不完全正确。虽然垃圾收集器将确保在您的应用程序关闭后清理所有内存,但它不能确保您在应用程序的生命周期内正确释放对象的引用。因此,您可能会在应用运行时遇到泄漏式问题 - 它们不会影响整个系统。
                      • 值得将这些情况与“真正的”内存泄漏区分开来,因为一种情况完全可以通过现有结构发现(您遍历对象图),另一种情况则相当复杂,因为如果您丢弃对一些分配的对象在释放它之前,你必须使用启发式方法来确定它是否是泄漏。
                      【解决方案14】:

                      如果任何一个终结器由于某种原因阻塞,实现终结器的类型可能会泄漏。由于锁定和线程单元问题,我已经看到终结器阻塞。

                      由于具有终结器的类型实例在它们各自的终结器运行之前不会被收集,因此单个阻塞终结器将阻止任何其他可终结的对象等待收集。

                      【讨论】:

                        【解决方案15】:

                        首先,让我分享一下我对内存泄漏的严格理解。我对内存泄漏的定义是当您分配了内存并且不再引用它时,因此无法释放该内存。话虽如此,.net 对象(我的意思是托管堆中的 CTS 类型的实例)中不可能发生内存泄漏。未引用的内存正是 GC 寻找释放的内存。

                        话虽如此,但人们可能对什么是内存泄漏有更松散的理解。如果您认为内存泄漏是正在使用的内存的不受控制的增长,那么这很容易。只是严重滥用静态变量,主要是那些引用巨大列表的变量。如果您保留这些对象的引用,GC 将永远不会清理它们,将它们提升到更高的世代并使它们更难收集。尽管这不是严格意义上的内存泄漏,但最终它可能会导致类似的症状。尝试检测这种“泄漏”的一个好方法是使用CLR Profiler

                        “泄漏”的另一个来源是如前所述不当使用事件处理程序。每当对象 A 向对象 B 的事件注册其实例方法之一时,对象 B 就会结束对对象 A 的间接引用,这意味着当 B 处于活动状态时,A 将保持活动状态。但是请注意,这里没有循环。只要 B 或 A 都没有任何根引用,无论它们有多少交叉引用,它们最终都会被收集。

                        最后,实际上可以在 .net 中引发内存泄漏,但在谈论托管内存时(至少理论上)永远不会,因为 GC 在清除它方面做得非常出色。如果您的任何对象维护对非托管内存的引用,例如通过互操作,则需要显式清理该内存。如果不这样做,确实会导致内存泄漏。尽管我从未经历过这种情况,但至少在理论上这可能会发生。如前所述,持有此类引用的对象应该实现 IDiposable 以清除内存,并且它们的使用应保证为此目的调用 Dispose,主要是通过使用 using 子句。请注意,Dispose 不会释放对象的内存,而只会要求对象释放它所引用的任何非托管内存。

                        一种特殊的非托管内存是在互操作场景中使用的 COM 对象所需要的。这些对象通过 Runtime Callable Wrappers、朋友的 RCW 访问,但没有 Dispose。 “使用”将不起作用。释放底层COM对象的方式是通过静态方法:

                        System.Runtime.InteropServices.Marshal.ReleaseComObject(object);
                        

                        由于“使用”只是在 finally 块中调用 IDisposable.Dispose() 的语法糖,它不能用于 RCW,因此不要忘记将调用放在自己的 finally 块中的 ReleaseComObject(object) 中,这样你确保它真的被调用了。

                        【讨论】:

                          【解决方案16】:

                          使用“using”关键字自动调用 IDisposable 对象的 Dispose() 方法。
                          对于任何 COM 互操作,您必须手动释放所有资源。

                          【讨论】:

                            【解决方案17】:

                            【讨论】:

                              猜你喜欢
                              • 1970-01-01
                              • 1970-01-01
                              • 2020-05-13
                              • 1970-01-01
                              • 2017-10-31
                              • 2018-04-08
                              • 2013-06-24
                              相关资源
                              最近更新 更多