在与同事讨论启动时使用近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
}
答案 0 :(得分:5)
我想首先澄清一下。 - 假设您正在运行应用程序作为32位应用程序,您的进程可用的VA空间仅为2 GB,如果您启用了大地址空间切换,则为3 GB,因此即使您有巨大的页面文件,如果您是32位也没关系过程,如果你运行64位,你有很大的地址空间,这很重要。
所以你会在其中一个案例中找回记忆。 您在达到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>