Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/.net/24.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# WeakReference是否能提供良好的缓存?_C#_.net_Caching_Garbage Collection_Weak References - Fatal编程技术网

C# WeakReference是否能提供良好的缓存?

C# WeakReference是否能提供良好的缓存?,c#,.net,caching,garbage-collection,weak-references,C#,.net,Caching,Garbage Collection,Weak References,我有一个缓存,它使用对缓存对象的weakreference使它们在内存紧张时自动从缓存中删除。我的问题是,缓存对象存储在缓存中后很快就会被收集。缓存在64位应用程序中运行,尽管仍有超过4gig的内存可用,但所有缓存对象都会被收集(此时它们通常存储在G2堆中)。如process explorer所示,没有手动引发的垃圾收集 我可以应用什么方法来延长对象的寿命?不,WeakReference不适合这样做,因为垃圾收集器的行为会随着时间的推移而改变,并且缓存不应该依赖于当前的行为。还有许多你无法控制的

我有一个缓存,它使用对缓存对象的weakreference使它们在内存紧张时自动从缓存中删除。我的问题是,缓存对象存储在缓存中后很快就会被收集。缓存在64位应用程序中运行,尽管仍有超过4gig的内存可用,但所有缓存对象都会被收集(此时它们通常存储在G2堆中)。如process explorer所示,没有手动引发的垃圾收集


我可以应用什么方法来延长对象的寿命?

不,WeakReference不适合这样做,因为垃圾收集器的行为会随着时间的推移而改变,并且缓存不应该依赖于当前的行为。还有许多你无法控制的因素会影响记忆压力

有许多.NET缓存的实现。你可以在CodePlex上找到一打。我想您需要添加的内容是查看应用程序的当前工作集,以将其用作清除的触发器


关于为什么频繁收集对象的另一个注意事项。GC在清理Gen0对象方面非常积极。如果您的对象非常短暂(直到对它的唯一引用是弱引用为止),那么GC正在尽可能快地进行清理,从而完成它设计的任务。

使用WeakReferences作为引用缓存对象的主要手段并不是一个好主意,因为正如Josh所说,您的未来行为将取决于WeakReference和GC的任何变化

但是,如果缓存需要任何类型的恢复功能,那么对挂起清除的项使用WeakReferences是很有用的。当项目满足逐出条件时,您可以将其引用更改为弱引用,而不是立即逐出它。如果在GC'ed之前有任何东西请求它,您可以恢复它的强引用,并且该对象可以再次活动。我发现这对于一些命中率模式难以预测的缓存非常有用,因为频繁的“复活”会带来好处


如果您有可预测的命中率模式,那么我将放弃WeakReference选项并执行显式逐出。

在.net中,从GC的角度来看,WeakReference根本不被视为引用,因此任何只有弱引用的对象都将在下一次GC运行中收集(用于适当的生成)

这使得弱引用完全不适合缓存—正如您的经验所示


您需要一个“真正的”缓存组件,而缓存最重要的事情是获取一个包含逐出策略的组件(即,关于何时从缓存中删除对象的规则)与应用程序的使用模式非常匹配。

有一种情况,基于
的WeakReference
缓存可能很好:类中某个项的有用性取决于对它的引用的存在。在这种情况下,弱的内部缓存可能很有用。例如,如果一个应用程序将反序列化许多大型不可变对象,其中许多对象预期是重复的,并且必须在它们之间执行许多比较。如果
X
Y
是对某个不可变类类型的引用,那么如果两个变量都指向同一实例,测试
X.Equals(Y)
将非常快,但如果它们指向恰好相等的不同实例,则测试可能非常慢。如果反序列化对象恰好与已存在引用的另一个对象匹配,则从字典中获取对后一个对象的引用(需要一次缓慢的比较)可能会加快将来的比较。另一方面,如果它匹配字典中的某个项,但字典是该项的唯一引用,那么使用字典对象而不是简单地保留读入的对象将没有什么好处;可能没有足够的优势来证明比较的成本。对于内部缓存,一旦对象不存在其他引用,让
WeakReferences
尽快失效将是一件好事。

答案实际上取决于您试图构建的缓存的使用特性。在我的许多项目中,我成功地使用了基于WeakReference的缓存策略来提高性能,在这些项目中,缓存对象预计将在短时间内多次读取中使用。正如其他人所指出的,从GC的角度来看,弱引用几乎是垃圾,在下一个GC循环运行时将被收集。这与内存利用率无关


但是,如果您需要一个能够在GC的这种野蛮行为中幸存下来的缓存,则需要使用或模拟System.Runtime.Caching命名空间提供的功能。请记住,当内存使用量超过阈值时,您需要一个额外的线程来清理缓存。

我相信您遇到的问题是,垃圾收集器移除弱引用对象不仅是为了响应内存压力,有时还会非常积极地进行收集只是因为运行时系统认为某些对象可能无法访问


您最好使用System.Runtime.Caching.MemoryCache,它可以配置内存限制,或者为项目自定义逐出策略。

有点晚了,但这里有一个相关的用例:

我需要缓存两种类型的对象:大的(反序列化的)数据文件,每个文件需要10分钟的加载时间和15G的ram;小的(动态编译的)对象包含对这些数据文件的内部引用(较小的对象也会被缓存,因为它们需要10秒的时间生成)。这些缓存隐藏在提供对象的工厂中(前一个组件不知道后一个组件),并且具有不同的逐出