如何从多个托管应用程序的大对象堆LOH中取回未使用的内存?

时间:2009-06-24 09:33:40

标签: c# .net garbage-collection

在与同事讨论启动时使用近1.5G内存的特定应用程序组时...他在.NET production debugging上指出了一个非常好的链接

困扰我的部分是......

  

例如,如果您分配1 MB   内存为单块,大   对象堆的大小​​扩展到1 MB。   当你释放这个对象时,大   对象堆不会解除   虚拟内存,所以堆保持在1   MB大小。如果你分配另一个   稍后500 KB块,新块是   在1 MB的块内分配   属于大对象的内存   堆。在整个过程中,   大对象堆总是保持不变   所有大块分配   目前引用,但从未   释放物体时收缩   即使发生垃圾收集。   下一页的图2.4显示了一个   大对象堆的示例。

现在假设我们有一个虚构的应用程序,可以创建一大堆大对象(> 85KB),所以大对象堆增长让我们说200兆。现在假设我们有10个这样的app实例正在运行..所以分配了2000 Megs。现在这个记忆永远不会回到操作系统,直到过程关闭......(就是我所理解的)

我的理解是否有任何差距?我们如何在各种LOHeaps中找回未使用的内存;我们不会创造OutOfMemoryExceptions的完美风暴?

更新:从Marc的回复中,我想澄清一下LOH对象没有被引用 - 大对象是use-n-throw - 但是即使堆是堆,堆也不会缩小最初激增后相对空洞。

更新#2:只是包含一个代码片段(夸大但我认为可以解决这个问题)..在虚拟内存达到我机器上的1.5G标记时,我看到一个OutOfMemoryException( 1.7G on another)..从Eric L.'s blog post开始,'进程内存可以被视为磁盘上的大量文件'' - 因此这个结果是出乎意料的。在这种情况下,机器在HDD上具有GB的可用空间。 PageFile.sys操作系统文件(或相关设置)是否有任何限制?

        static float _megaBytes;
        static readonly int BYTES_IN_MB = 1024*1024;

        static void BigBite()
        {
           try
           {
              var list = new List<byte[]>();
              int i = 1;

              for (int x = 0; x < 1500; x++)
              {
                 var memory = new byte[BYTES_IN_MB + i];
                 _megaBytes += memory.Length / BYTES_IN_MB;
                 list.Add(memory);
                 Console.WriteLine("Allocation #{0} : {1}MB now", i++, _megaBytes);
              }
           }
           catch (Exception e)
           {  Console.WriteLine("Boom! {0}", e); // I put a breakpoint here to check the console
              throw;
           }
        }
       static void Main(string[] args)
        {
            BigBite();
            Console.WriteLine("Check VM now!"); Console.ReadLine();
            _megaBytes = 0;

            ThreadPool.QueueUserWorkItem(delegate { BigBite(); });
            ThreadPool.QueueUserWorkItem(delegate { BigBite(); });
            Console.ReadLine();   // will blow before it reaches here
        }

3 个答案:

答案 0 :(得分:5)

我想首先澄清一下。 - 假设您正在运行应用程序作为32位应用程序,您的进程可用的VA空间仅为2 GB,如果您启用了大地址空间切换,则为3 GB,因此即使您有巨大的页面文件,如果您是32位也没关系过程,如果你运行64位,你有很大的地址空间,这很重要。

  • 尺寸为&gt的对象;在LOH上分配 85000字节,注意它是 85000字节而不是85K,它也是可以改变的实现细节。 现在,回到你的问题。 GC将取消提交在两种情况下未使用的LOH段 1-当机器上的内存压力很高时(~95-98%) 2-当它无法满足新的分配请求时,它将解除LOH中未使用的页面

所以你会在其中一个案例中找回记忆。 您在达到2GB限制之前击中OOM的事实可能意味着您有VA碎片,当您没有连续的VA地址空间来满足新的分配时会发生VA碎片,例如您要求8KB段,并且您不要你的VA连续2页(假设页面大小为4 K)

你可以在Windows的调试工具中使用!vamap调试器扩展来验证这一点。

希望这会有所帮助 感谢

答案 1 :(得分:3)

如果LOH想要保留内存,那就是LOH - 但是,不要忘记OutOfMemoryException是每个进程,因为硬盘确实是虚拟内存的限制因素。 Eric Lippert blogged about this最近。当然,这并不妨碍它从所有分页中获得较差的性能......

答案 2 :(得分:2)

好吧,如果你真的有这种分配模式,你可以将大对象移动到另一个appdomain - 当你决定释放所有大对象时,释放appdomain并释放该appdomain的堆。 / p>