.Net 4 MemoryCache泄漏并发垃圾收集

我在.Net 4中使用了新的MemoryCache ,最大高速caching大小限制为MB(我testing了它在10到200MB之间,在1.75到8GB内存的系统上)。 我不会在对象上设置任何基于时间的过期,因为我只是将高速caching用作高性能驱动器,只要存在空间,我希望使用它。 令我惊讶的是,caching拒绝驱逐任何对象,以至于我会得到SystemOutOfMemoryexception

我启动了perfmon ,将我的应用程序连接到.Net CLR Memory\#Bytes In All Heaps.Net Memory Cache 4.0Process\Private Bytes – 实际上,内存消耗已经失控,没有caching修剪注册。

做了一些谷歌searchstackoverflowing ,下载和附加CLRProfiler ,和wham :到处驱逐! 内存保持在合理的范围内,根据我设定的内存大小限制。 在debugging模式下再次运行,不会驱逐。 CLRProfiler再次驱逐。

我终于注意到,探查器强制应用程序运行,没有并发垃圾收集 (也见有用的SO并发垃圾收集问题 )。 我在我的app.config中closures了它,果然,驱逐!

这似乎充其量只是一个荒谬的缺乏文档不说: 这只适用于非并发垃圾收集虽然我的形象,因为它从ASP.NET移植,他们可能不必担心并发垃圾收集

那么有没有其他人看到这个? 我很想在那里得到一些其他的经验,也许还有一些更有教养的见解。


更新1

我已经在一个单一的方法中重现了这个问题:看起来caching必须并行写入caching驱逐不能触发(在并发垃圾收集模式下)。 如果有兴趣的话,我会把testing代码上传到公开的回购站。 我肯定正在进入CLR / GC / MemoryCache池的深层,我想我忘了我的floaties …


更新2

我在CodePlex上发布了testing代码来重现这个问题。 此外,可能有趣的是,原始生产代码作为工作者angular色在Azure中运行。 有趣的是,更改angular色的app.config中的GC并发设置没有任何作用。 可能Azure重写GC设置很像ASP.NET? 此外,在WPF和控制台应用程序下运行testing代码将产生稍微不同的驱逐结果。

您可以在有问题的方法之后立即“强制”垃圾回收,看看问题是否重现执行:

 System.Threading.Thread.Sleep(200); GC.Collect(); GC.WaitForPendingFinalizers(); 

在该方法的最后(确保释放任何句柄来引用对象并将它们清空)。 如果这可以防止内存泄漏,那么是的,可能会有一个运行时错误。

世界停止垃圾收集的基础是确定在世界停止的时刻是否存在强有力的对象活引用。 并发垃圾收集通常决定一个对象的强有力的引用在过去的某个特定时间是否已经存在。 我的猜想是许多强引用的WeakReferences中的对象被单独创build和丢弃。 如果一个停止世界的垃圾收集器在创build一个特定的对象和抛弃它的时间之间触发,那么这个特定的对象将保持活动状态,但是以前丢弃的对象不会被激活。 相比之下,一个并发垃圾收集器可能不会检测到一个对象的所有强引用都被丢弃,直到一段时间过去,而没有任何对该对象的强引用被创build。

有时候我会希望.net能提供一个强大的参考和一个弱的参考,这样可以防止一个对象被从内存中抹去,但是不能保护它不被最终确定,或者使弱引用无效。 这样的引用会使GC过程稍微复杂一些,要求每个对象都有独立的标志,表示是否存在强和弱的引用,以及是否已经扫描了强引用和准弱引用,但是这样的function可能有帮助在很多“弱势事件”的情况下。

我在search一个类似的主题时发现了这个条目,我正在关注你的内存exception。

如果你把一个对象放在caching中,那么它仍然可能引用其他对象,因此这些对象不会被垃圾收集 – 因此,内存溢出exception,可能是因为第2代垃圾收集而被挂起的CPU。

您是否将caching中的“已使用”对象或caching上已使用对象的克隆? 如果你在caching上放置一个克隆,那么可能引用其他对象的“used”对象可能被垃圾回收。

如果你closures你的caching机制,你的程序是否仍然用完内存? 如果内存没有用完,那么这将certificate你将放在caching中的对象仍然持有对阻碍垃圾收集的其他对象的引用。

强制垃圾收集不是最佳实践,不应该做。 在这种情况下强制垃圾回收不会处理引用的对象。

MemoryCache肯定有一些问题。 它在我的asp.net服务器上吃了160Mb的内存,只是改成了简单的列表,并添加了一些额外的逻辑来获得相同的function。