我有一个使用WeakReferences来缓存对象的缓存,以便在出现内存压力时自动从缓存中移除。我的问题是,缓存的对象在存储后很快就被回收了。该缓存在64位应用程序中运行,尽管仍有超过4GB的内存可用,所有缓存的对象都被回收了(通常此时它们存储在G2堆中)。根据进程资源监视器没有手动触发垃圾回收。
我可以采用哪些方法使对象存活时间变长?
我可以采用哪些方法使对象存活时间变长?
使用WeakReferences作为引用缓存对象的主要手段并不是一个好主意,因为正如Josh所说,你完全被依赖于WeakReference和GC任何未来行为变化的掌控之中。
然而,如果您的缓存需要任何形式的恢复能力,则对于待清除的项目使用WeakReferences是有用的。当一个项目满足驱逐条件时,而不是立即将其驱逐,您可以将其引用更改为弱引用。如果在它被GC之前有任何请求,您可以恢复它的强引用,这个对象就可以再次存在。我发现这对于一些具有难以预测命中率模式和足够频繁的“复活”操作的缓存非常有用。
如果您有可预测的命中率模式,那么我建议放弃WeakReference选项并执行显式清除操作。
WeakReference
的缓存可能很好用:当类中某个项的有用性取决于对它的引用是否存在时。在这种情况下,弱引用缓存可能很有用。例如,如果一个应用程序将反序列化许多大型不可变对象,其中许多对象预计是重复的,并且必须在它们之间执行许多比较。如果X
和Y
是对某个不可变类类型的引用,在两个变量都指向同一实例时,测试X.Equals(Y)
会非常快,但如果它们指向相等的不同实例,则可能非常慢。如果反序列化对象恰好与已存在引用的另一个对象匹配,则从字典中获取对该后者对象的引用(需要进行一次缓慢的比较)可能加速未来的比较。另一方面,如果它与字典中的某个项匹配,但字典是该项的唯一引用,则使用字典对象而不是仅保留读入的对象的优势很小;可能没有足够的优势来证明比较的成本。对于一个内部缓存,使WeakReferences
尽快失效,一旦没有其他引用存在于对象中,这是一件好事。答案实际上取决于您正在构建的缓存的使用特征。在我的许多项目中,我已经成功地使用了基于WeakReference的缓存策略来提高性能,其中缓存的对象预计会在短时间内进行多次读取。正如其他人指出的那样,从GC的角度来看,弱引用基本上是垃圾,并且将在下一个GC周期运行时收集。这与内存利用无关。
然而,如果您需要一个可以经受住GC的残酷对待的缓存,您需要使用或模仿System.Runtime.Caching命名空间提供的功能。请记住,当内存使用量超过您的阈值时,您需要一个额外的线程来清理缓存。
因此,尽管仅使用WeakReference(而不是缓存)是一个糟糕的想法(因为现代GC通常调整到L2 CPU缓存的大小,并且常规代码将每分钟多次通过此缓存),但它非常有用作为一种方法来隐藏您的缓存,使其不受代码的其余部分的影响。