.NET可用内存使用情况(如何防止OS的内存/释放内存)

时间:2012-03-21 12:53:58

标签: c# .net memory-management garbage-collection clr

我目前正在开发一个大量使用缓存数据的网站,以避免往返。 在启动时,我们得到一个“大”图(数百个不同类型的对象)。 这些对象通过WCF检索并反序列化(我们使用协议缓冲区进行序列化) 我正在使用redgate的内存分析器调试内存问题(在我们完成初始化并结束此报告之后,内存似乎与我们应该需要多少内存不相符

Global Report

现在我们可以从这份报告中得到的结论是:

1)所分配的大部分内存都是免费的(它可能在反序列化期间被正确分配,但现在它是免费的,我希望它能够返回到操作系统)

2)内存碎片化(这很糟糕,因为每当我刷新现金时我需要重做内存饥饿的反序列化过程,这反过来会创建大型对象,可能因碎片而抛出OutOfMemoryException)

3)我不知道为什么空间是碎片化的,因为当我看到大对象堆时,只有30个实例,15个object []直接连接到GC并且与我完全无关,1是char数组也直接附加到GC Heap,剩下的15个是我的,但不是原因,因为如果我在代码中注释掉它,我会得到相同的报告。

所以我的问题是,我该怎么做才能更进一步呢?我不太确定在调试/工具中要找什么,因为看起来我的内存是碎片化的,但不是我的,并且.net分配了大量的可用空间,我无法释放。

另外请确保你在回答之前理解这个问题,我不是想找到一种在.net(GC.Collect)中释放内存的方法,而是将.net中已经免费的内存释放到系统中以及对记忆进行碎片整理。

请注意,缓慢的解决方案很好,如果可以手动对大型堆进行碎片整理,那么我可以在RefreshCache的末尾调用它,如果运行需要1或2秒,则可以。

感谢您的帮助!

我忘记了一些笔记: 1)该项目是.net 2.0网站,我在.net 4池中运行相同的结果,如果我在.net 4池中运行它并将其转换为.net 4并重新编译。

2)这些是发布版本的结果,因此调试版本不是问题。

3)这可能非常重要,我在webdev服务器上根本没有得到这些问题,只在IIS中,在webdev中我的内存消耗量与我的实际消耗量相当接近(更多,但不是5-多10倍!)

6 个答案:

答案 0 :(得分:6)

在大对象堆上分配的对象(对象> = 85,000字节,通常是数组)不会被垃圾收集器压缩。微软决定移动这些对象的成本太高了。

  

建议尽可能重用大对象以避免   托管堆和VM空间上的碎片。

http://msdn.microsoft.com/en-us/magazine/cc534993.aspx

我假设您的大对象是由反序列化库创建的临时字节数组。如果库允许您提供自己的字节数组,则可以在程序开始时预先分配它们,然后重复使用它们。

答案 1 :(得分:4)

我知道这不是你想听到的答案,但你不能强行将内存释放回操作系统。但是,你想要这样做的原因是什么?一旦物理内存不足,.NET就会将其堆释回操作系统。但是如果有足够的可用物理内存,.NET将保留其堆,以便更快地分配对象。如果你真的想强制.NET将其堆释放回操作系统,我想你可以编写一个只有malloc的C程序,直到它耗尽内存。这应该导致操作系统发出信号通知.NET以释放其未使用的堆部分。

最好为.NET重新使用未使用的内存,以便您的应用程序具有更好的分配性能(因为运行时知道哪些内存是空闲的,哪些不是,分配只需使用空闲内存而无需系统调用操作系统获得更多内存。)

垃圾收集器负责对堆进行碎片整理。每隔一段时间(通常在集合运行期间),如果确定需要完成此操作,它将在堆周围移动对象。 (这就是为什么C ++ / CLI具有用于“固定”对象的pin_ptr构造的原因。)

碎片通常对内存来说不是一个大问题,因为它提供了快速的随机访问。

至于你的OutOfMemoryException,我没有一个好的答案。通常我怀疑你的旧对象图没有被收集(某个对象某处有一个引用它,一个“内存泄漏”)。但是既然你正在使用一个分析器,我当时就不知道。

答案 2 :(得分:2)

一些测试和一些C ++之后,我发现了为什么我得到这么多可用内存的原因,这是因为IIS通过VM Hoarding实例化CLR(提供一个dll来实例化它而不需要VM Hoarding占用尽可能多的初始内存,但随着时间的推移释放大部分时间,这是我期望的行为)。 所以这确实解决了我报告的内存问题,但无论如何我仍然可以获得大约100mb的可用内存,我仍然认为这是由于碎片和片段只是立即发布,因为探查器仍然报告内存碎片。因此,不要将自己的答案标记为答案,希望有人可以对此有所了解或指导我使用可以修复此问题或帮助我调试根本原因的工具。

答案 3 :(得分:2)

从.NET 4.5.1开始,您可以在发出对GC collect的调用之前设置一次性标志来压缩LOH,即

Runtime.GCSettings.LargeObjectHeapCompactionMode = System.Runtime.GCLargeObjectHeapCompactionMode.CompactOnce; 所以GC.Collect(); //这将导致LOH被压缩(一次)。

答案 4 :(得分:1)

令人感兴趣的是,它在WebDevServer上的工作方式与IIS相同......

IIS是否可能使用服务器垃圾收集器,而WebDev服务器是否可能使用工作站垃圾收集器?垃圾收集的方法可能会影响碎片。它可能会在您的aspnet.config文件中设置。请参阅:http://support.microsoft.com/kb/911716

答案 5 :(得分:0)

如果您没有找到答案,我认为以下线索可以帮助您:

回到基础:我们有时会忘记可以显式设置对象,显式调用对象的Dispose方法(因为你没有提到它,我想你做了一个“object = null“指令代替。”

使用继承的方法,你不需要实现一个,除非你的类没有它,我怀疑它。

MSDN帮助说明了这种方法:

  

... 实施Dispose没有性能优势   仅使用托管资源(如数组)的类型的方法   因为垃圾收集器会自动回收它们。使用   Dispose方法主要在使用native的托管对象上   资源以及暴露给.NET的COM对象   框架。 ......

因为它说“它们被垃圾收集器自动回收”我们可以推断当调用该方法时“释放的东西”(再次我只想给你提供线索)。

除此之外,我发现了这篇有趣的文章(我想......我没看过......完全):垃圾收集:Microsoft .NET Framework中的自动内存管理(http://msdn.microsoft.com/en-us/magazine/bb985010.aspx),其中说明了以下内容: “ 强制对象清理 ”部分:

  

...,还建议您添加其他方法   允许该类型的用户明确清理的类型   对象何时需要。按照惯例,应该调用此方法   关闭或处置....

如果你仔细阅读或者只是继续调查这个方向,也许答案就在于这篇文章。