在与同事讨论一组应用程序在启动时使用了近1.5G的内存时...他给我指了一个非常好的链接.NET生产调试。
现在假设我们有一个虚构的应用程序,可以创建大量的对象(> 85KB),因此大对象堆增长到200兆。现在假设我们有10个这样的应用程序实例正在运行...那么就分配了2000兆。那么这个内存直到进程关闭才不会被返回给操作系统...(这是我理解的)例如,如果您向单个块分配1 MB内存,则大对象堆将扩展为1 MB大小。当您释放此对象时,大对象堆不会取消提交虚拟内存,因此堆的大小保持为1 MB。如果稍后分配另一个500-KB块,则新块将分配在属于大对象堆的1 MB内存块中。在进程的生命周期内,大对象堆始终增长以容纳当前引用的所有大块分配,但即使进行垃圾回收,它也永远不会在对象释放时缩小。图2.4在下一页上显示了大对象堆的示例。
我的理解有什么漏洞吗?我们如何回收各种LOH堆中未使用的内存;我们如何避免发生OutOfMemoryExceptions的完美风暴?
更新:从Marc的回答中,我想澄清一下,LOH对象没有被引用-大型对象是使用和丢弃的-然而,尽管堆相对空闲,但堆不会缩小。
更新#2:仅包含一个代码片段(夸张但我认为可以表达重点)... 我在我的机器上看到一个OutOfMemoryException,大约在虚拟内存达到1.5G标记时(另一台机器上为1.7G)... 来自Eric L.'s blog post,“进程内存可以被视为磁盘上的大型文件..”- 因此这个结果是意外的。在这种情况下,这些机器在硬盘上有几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
}