代码之家  ›  专栏  ›  技术社区  ›  Kevin Up

如何使用IDisposable修复内存泄漏

  •  6
  • Kevin Up  · 技术社区  · 15 年前

    我有一个.NET应用程序似乎有内存泄漏问题。.NET服务启动时大约有100MB的内存,但在负载情况下,它会达到400-500MB左右。我的大多数类没有非托管资源,而那些已经实现了IDisposable的类。那么,我的问题是,在我的课上打我可以得到帮助吗?

    4-500 MB本身并不重要。问题是有8种不同的服务。每一个都是使用sharparch、nservicebus、windsor和nhibernate建造的。我的感觉是其中的一个问题导致了一个问题。我担心的是,所有服务的总内存大约是4个gig内存中的3.2到3.6 gigs。它还没有抛出内存异常,但我想在传递时阻止它。另外,我还使用了dottrace,它为我提供了一些信息,我只是不确定如何对这些信息采取行动。

    5 回复  |  直到 15 年前
        1
  •  17
  •   Eric Lippert    15 年前

    我首先要考虑的是确保您正在测量相关的东西。”“记忆”可能意味着很多不同的事情。有一个 巨大的 虚拟内存空间不足和RAM不足之间的差异。有一个 巨大的 由页面文件振荡引起的性能问题和由创建太多GC压力引起的性能问题之间的差异。

    如果你不理解RAM、虚拟内存、工作集和页面文件之间的关系,那么从阅读开始,直到你理解了所有这些内容。你表达问题的方式让我怀疑你相信虚拟内存和RAM是一样的。 当然不是。

    我怀疑你正在做的算术是:

    • 我有八个进程,每个进程消耗5亿字节的虚拟地址空间
    • 我有40亿字节的内存
    • 因此,我将要得到一个OutofMemory异常

    三段论完全无效。这就是三段论:

    • 我有八夸脱冰淇淋
    • 我有空在冰箱里放九夸脱冰淇淋
    • 因此,如果我再吃两夸脱冰淇淋,就会有东西融化。

    when in fact you have an entire warehouse-sized cold storage facility next door. Remember, RAM is just a convenient fast way to store stuff near where you need it, like your fridge. 如果你有更多的东西需要储存,谁会在乎你是否在本地用完了房间?You can always pop next door and put the stuff you use less frequently in long term deep freeze -- the page file. 那就少了 方便的 但是 没有融化 .

    当一个进程耗尽虚拟地址空间时,您会得到一个“内存不足”的异常,而不是当系统中的所有RAM都被占用时。当系统中的所有RAM都被消耗掉时,您不会得到错误,您会得到 废话表演 因为操作系统花费了所有的时间在磁盘上来回运行东西。

    所以,不管怎样,首先要了解您正在测量的内容以及Windows中的内存是如何工作的。你真正需要的是:

    • 在32位系统上使用超过20亿字节的虚拟内存有危险吗?一个进程只获得2GB的虚拟内存(不是RAM,记住,虚拟内存与RAM无关: that's why its called "virtual" -- it isn't hardware )在用户代码可寻址的win32上;如果您尝试使用更多,您将获得一个OOM。

    • 是否有任何进程有可能试图分配一个巨大的虚拟内存块,从而使该大小的连续块不可用?例如,您是否可能在单个数组中分配一千万字节的数据?再来一次,OOM。

    • 工作集 --也就是说,由于性能原因,一个进程的虚拟内存页*必须在RAM中,而所有进程都小于可用RAM的数量?如果没有,那么很快你就会被打到,但不会被打到。

    • 您的页面文件是否足够大,能够处理在RAM开始变短时可能被调出到磁盘的虚拟内存页面?

    到目前为止,这些都与.NET无关。一旦你确定 真实的 问题-可能不存在-然后根据实际问题开始调查。使用内存分析器检查内存分配器和垃圾收集器正在做什么。查看大型对象堆中是否存在巨大的块,或者无法收集的活动对象的意外大图形,或者是什么。但要运用好的工程原理:了解系统,使用工具调查实际的经验性能,用变化进行实验,并仔细测量其结果。不要只是在一些类上随机地添加magic-idisposable接口,并希望这样做会使问题(如果有)消失。

        2
  •  15
  •   JaredPar    15 年前

    如果具有非托管资源的所有类实现 IDisposable 并被正确处置(通过使用或尝试/最终),然后进一步添加 不可分的 实现不会有任何帮助。

    第一个问题是你不知道为什么会漏水。托管应用程序通常由于以下原因之一泄漏

    1. Not properly disposing of unmanaged resources
    2. 抓住管理对象的大对象图

    考虑到问题中的信息,几乎可以肯定是2造成了问题。您需要一个分析器或windbg来告诉您实际的泄漏是什么以及哪些根对象导致了泄漏。

    这是里科的一篇很好的文章

        3
  •  5
  •   Rob    15 年前

    答案是,几乎肯定不是。通过分析您的服务,您是否已经验证了您确实拥有不应该拥有的内存?

    记住,垃圾收集器在需要释放内存之前可能不一定释放内存,因此,分配到400-500MB的内存可能并不罕见。我担心的是,在[在这里插入合理的时间段]使用之后,它会进一步爬升并达到1GB,即使它没有处于任何更高的负载水平。

        4
  •  4
  •   x0n    15 年前

    简短回答:没有。

    更长的回答:没有。

    我想你已经知道了——如果你不知道,你就不会在需要它的类上“扇”IDisposable——IDisposable与GC无关。这里真正重要的是,如果您不必要地在对象上放置了终结器(~classname),这将导致它们在获得GC之前被备份到单线程终结器队列中,而不管它们是否包含非托管资源。

        5
  •  2
  •   Sam Saffron James Allen    15 年前

    测量,测量,测量

    如果你想减少你的应用程序的内存使用,你需要先确定它在哪里使用。

    您可以通过在“private bytes,bytes in all heaps,bytes in large object heap,gen 1,gen 2”上添加几个性能计数器来大致了解这一点。

    如果确定使用的托管内存太多。您可以使用类似的工具进一步分解用法。 .Net memory profiler 或者非常灵活但是 windbg + sos

    一旦您隔离了内存的去向,您就可以查看减少使用的策略,这可以是一个简单的方法,例如用缓存替换字典或添加字符串生成器。

    解决方案不太可能洒在任何东西上。