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

发布于 2024-07-22 19:34:00 字数 202 浏览 7 评论 0原文

我有一个缓存,它使用对缓存对象的弱引用,使它们在出现内存压力时自动从缓存中删除。 我的问题是,缓存的对象在存储到缓存中后很快就会被收集。 缓存在 64 位应用程序中运行,尽管仍然有超过 4gig 的内存可用,但所有缓存的对象都会被收集(此时它们通常存储在 G2 堆中)。 正如进程资源管理器所示,没有手动引发垃圾收集。

有什么方法可以让物体的寿命延长一点呢?

i have a cache that uses WeakReferences to the cached objects to make them automatically removed from the cache in case of memory pressure. My problem is that the cached objects are collected very soon after they have been stored in the cache. The cache runs in a 64-Bit application and in spite of the case that more than 4gig of memory are still available, all the cached objects are collected (they usually are stored in the G2-heap at that moment). There are no garbage collection induced manually as the process explorer shows.

What methods can i apply to make the objects live a litte longer?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(7

ま柒月 2024-07-29 19:34:00

使用 Wea​​kReferences 作为引用缓存对象的主要方式并不是一个好主意,因为正如 Josh 所说,您会受到 WeakReference 和 GC 未来任何行为变化的影响。

但是,如果您的缓存需要任何类型的复活功能,则对待清除的项目使用 Wea​​kReferences 会很有用。 当某个项目满足逐出标准时,您不是立即逐出它,而是将其引用更改为弱引用。 如果有任何东西在 GC 之前请求它,您可以恢复它的强引用,并且该对象可以再次存活。 我发现这对于一些难以预测命中率模式且足够频繁的“复活”的缓存很有用。

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

Using WeakReferences as the primary means of referencing cached objects is not really a great idea, because as Josh said, your at the mercy of any future behavioral changes to WeakReference and the GC.

However, if your cache needs any kind of resurrection capability, use of WeakReferences for items that are pending purge is useful. When an item meets eviction criteria, rather than immediately evicting it, you change its reference to a weak reference. If anything requests it before it is GC'ed, you restore its strong reference, and the object can live again. I have found this useful for some caches that have hard to predict hit rate patterns with frequent enough "resurrections" to be beneficial.

If you have predictable hit rate patterns, then I would forgoe the WeakReference option and perform explicit evictions.

征棹 2024-07-29 19:34:00

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

There is one situation where a WeakReference-based cache may be good: when the usefulness of an item in the class is predicated upon the existence of a reference to it. In such a situation, a weak interning cache may be useful. For example, if one had an application which would deserialize many large immutable objects, many of which were expected to be duplicates, and would have to perform many comparisons between them. If X and Y are references to some immutable class type, testing X.Equals(Y) will be very fast if both variables point to the same instance, but may be very slow if they point to distinct instances that happen to be equal. If a deserialized object happens to match another object to which a reference already exists, fetching a from the dictionary a reference to that latter object (requiring one slow comparison) may expedite future comparisons. On the other hand, if it matched an item in the dictionary but the dictionary was the only reference to that item, there would be little advantage to using the dictionary object instead of simply keeping the object that was read in; probably not enough advantage to justify the cost of the comparison. For an interning cache, having WeakReferences get invalidated as soon as possible once no other references exist to an object would be a good thing.

幻想少年梦 2024-07-29 19:34:00

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

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

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

In .net, a WeakReference is not considered a reference from the GC standpoint at all, so any object that only has weak references will be collected in the next GC run (for the appropriate generation).

That makes weak reference completely inappropriate for caching - as your experience shows.

You need a "real" cache component, and the most important thing about caching is to get one where the eviction policy (that is, the rules about when to drop an object from the cache) are a good match for you application's usage pattern.

琉璃繁缕 2024-07-29 19:34:00

不,WeakReference 对此并不好,因为垃圾收集器的行为可能并且会随着时间的推移而改变,并且您的缓存不应该依赖于当前的行为。 此外,许多您无法控制的因素也可能会影响内存压力。

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

关于为什么您的物品被如此频繁地收集的另一说明。 GC 在清理 Gen0 对象时非常积极。 如果您的对象的生命周期非常短(直到对它的唯一引用是弱引用),那么 GC 就会通过尽快清理来完成其设计目的。

No, WeakReference is not good for that because the behavior of the garbage collector can and will change over time and your cache should not be relying on today's behavior. Also many factors outside of your control could affect memory pressure.

There are many implementations of a cache for .NET. You could find probably a dozen on CodePlex. I guess what you need to add to it is something that looks at the application's current working set to use that as a trigger for purging.

One more note about why your objects are being collected so frequently. The GC is very aggressive at cleaning up Gen0 objects. If your objects are very short-lived (up until the only reference to it is a weak reference) then the GC is doing what it's designed to do by cleaning up as quickly as it can.

木緿 2024-07-29 19:34:00

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

您可能最好使用例如 System.Runtime.Caching.MemoryCache,它可以配置内存限制或项目的自定义驱逐策略。

I believe the problem you are having is that the Garbage Collector removes weakly referenced objects in response not only in response to memory pressure - instead it will do collection quite aggressively sometimes just because the runtime system thinks some objects may likely have become unreachable.

You may be better off using e.g. System.Runtime.Caching.MemoryCache which can be configured with a memory limit, or custom eviction policies for the items.

青萝楚歌 2024-07-29 19:34:00

答案实际上取决于您尝试构建的缓存的使用特征。 我已经成功地使用基于 WeakReference 的缓存策略来提高许多项目的性能,其中缓存的对象预计在多次读取的短突发中使用。 正如其他人指出的那样,从 GC 的角度来看,弱引用几乎是垃圾,并且会在下一个 GC 周期运行时被收集。 与内存利用率无关。

但是,如果您需要一个能够承受 GC 的残酷攻击的缓存,则需要使用或模仿 System.Runtime.Caching 命名空间提供的功能。 请记住,当内存使用量超过阈值时,您需要一个额外的线程来清理缓存。

The answer actually depends on usage characteristics of the cache you are trying to build. I have successfully used WeakReference based caching strategy for improving performance in many of my projects where the cached objects are expected to be used in short bursts of multiple reads. As others pointed out, the weak references are pretty much garbage from GC's point of view and will be collected whenever the next GC cycle is run. It's nothing to do with the memory utilization.

If, however, you need a cache that survives such brutality from GC, you need to use or mimic the functionality provided by System.Runtime.Caching namespace. Keep in mind that you'd need an additional thread that cleans up the cache when the memory usage is crossing your thresholds.

那请放手 2024-07-29 19:34:00

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

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

当我的“数据文件”缓存驱逐一个对象时,它会用弱引用替换它,因此如果该对象在下次请求时仍然可用,我们可以复活它(并更新其缓存超时)。 通过这种方式,我们可以避免在任何对象真正失效(即未在其他地方使用)之前丢失(或意外复制)任何对象。 请注意,两个缓存都不需要知道另一个,并且其他客户端对象不需要知道有任何缓存(例如:我们避免需要“keepalives”、回调、注册、检索和返回范围)等 - 事情变得简单多了)。

因此,虽然单独使用 Wea​​kReference(而不是缓存)是一个糟糕的主意(因为现代 GC 通常会根据 L2 CPU 缓存的大小进行调整,并且常规代码每分钟会烧毁这么多次),但它作为一种非常有用的方法从代码的其余部分隐藏缓存的方法。

A bit late, but here's a relevant use case:

I need to cache two types of objects: large (deserialised) data files that take 10 minutes to load and cost 15G of ram each, and smaller (dynamically compiled) objects that contain internal references to those data files (the smaller objects are also cached because they take ~10s to generate). These caches are hidden within the factories that supply the objects (the former component having no knowledge of the latter), and have different eviction policies.

When my `data file' cache evicts an object, it replaces it by a weak reference, so if that object is still available when next requested, we can resurrect it (and renew its cache timeout). In this way we avoid losing (or accidentally duplicating) any object before it is truly defunct (i.e. not used anywhere else). Notice that neither cache is required to be aware of the other, and that no other client objects need to be aware that there are any caches at all (eg: we avoid needing 'keepalives', callbacks, registration, retrieve-and-return scopes, etc - things get a lot simpler).

So although using WeakReference by itself (instead of a cache) is a terrible idea (because modern GCs are typically tuned to the size of the L2 CPU cache, and regular code will burn through this many times per minute), it's very useful as a way to hide your caches from the rest of your code.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文