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

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

全球报告

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

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

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

3)我不知道为什么空间是碎片的,因为当我看到大对象堆时,只有30个实例,15个object []直接连接到GC并且与我完全无关,1也是char数组直接附加到GC堆,其余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倍! )

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

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

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

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

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

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

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

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

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

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

令人感兴趣的是它在WebDevServer和IIS上的工作方式不同……

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

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

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

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

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

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

MSDN帮助说明了这种方法:

在仅使用托管资源(例如数组)的类型上实现Dispose方法没有性能优势,因为垃圾收集器会自动回收这些资源。 主要对使用本机资源的托管对象和公开给.NET Framework的COM对象使用Dispose方法。

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

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

…,还建议您为该类型添加其他方法,以允许该类型的用户在需要时显式清理对象。 按照惯例,这个方法应该叫做Close或Dispose ….

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