26

我需要使用可用 RAM 的一部分在 Java 中缓存对象。我知道其他人已经问过这个问题,但没有一个回答符合我的要求。

我的要求是:

  • 简单轻便
  • 并不比普通的 HashMap 慢很多
  • 使用 LRU,或一些近似于 LRU 的删除策略

我尝试了 LinkedHashMap,但是它要求您指定最大元素数,而且我不知道填充可用 RAM 需要多少元素(它们的大小会相差很大)。

我目前的方法是使用 Google Collection 的 MapMaker,如下所示:

Map<String, Object> cache = new MapMaker().softKeys().makeMap();

这看起来很有吸引力,因为它应该在需要更多 RAM 时自动删除元素,但是有一个严重的问题:它的行为是填满所有可用的 RAM,此时 GC 开始颠簸,整个应用程序的性能急剧下降。

我听说过 EHCache 之类的东西,但对于我需要的东西来说它似乎很重,而且我不确定它对于我的应用程序是否足够快(记住该解决方案不能比 HashMap 慢得多) .

4

12 回答 12

8

我对你有类似的要求——并发(在 2 个六核 CPU 上)和 LRU 或类似的——还尝试了 Guava MapMaker。我发现 softValues() 比 weakValues() 慢得多,但是当内存填满时,两者都让我的应用程序变得异常缓慢。

我尝试了 WeakHashMap,它的问题更少,甚至比通过其 removeEldestEntry() 方法使用 LinkedHashMap 作为 LRU 缓存更快。

但对我来说最快的是ConcurrentLinkedHashMap,它使我的应用程序比我尝试过的任何其他缓存快 3-4 (!!) 倍。快乐,经过几天的挫折!它显然已被合并到 Guava 的 MapMaker 中,但 LRU 功能无论如何都没有在 Guava r07 中。希望对你有效。

于 2010-10-04T00:17:47.533 回答
4

我已经实现了 serval 缓存,它可能和实现一个新的数据源或线程池一样困难,我的建议是使用 jboss-cache 或另一个众所周知的缓存库。所以你会睡得很好没有问题

于 2010-01-29T00:27:07.093 回答
3

我听说过 EHCache 之类的东西,但对于我需要的东西来说它似乎很重,而且我不确定它对于我的应用程序是否足够快(记住该解决方案不能比 HashMap 慢得多) .

我真的不知道是否可以说EHCache是重量级的。至少,我不认为 EHCache 是这样的,尤其是在使用内存存储时(它由扩展支持LinkedHashMap,当然是最快的缓存选项)。你应该试一试。

于 2010-01-29T08:13:57.140 回答
2

我相信MapMaker这将是获得您所要求的唯一合理的方式。如果“GC 开始抖动并且整个应用程序的性能急剧下降”,您应该花一些时间正确设置各种调整参数。这份文档乍一看可能有点吓人,但实际上写得非常清楚,是有关 GC 有用信息的金矿:

https://www.oracle.com/technetwork/java/javase/memorymanagement-whitepaper-150215.pdf

于 2010-01-29T19:32:29.457 回答
1

我不知道这是否是一个简单的解决方案,特别是与 EHCache 或类似的相比,但你看过Javolution 库吗?它不是为此而设计的,但在javolution.context包中它们有一个分配器模式,可以重用对象而无需垃圾收集。通过这种方式,它们将对象创建和垃圾收集降至最低,这是实时编程的一个重要特性。也许您应该看一下并尝试使其适应您的问题。

于 2010-01-29T09:35:38.767 回答
0

这看起来很有吸引力,因为它应该在需要更多 RAM 时自动删除元素,但是有一个严重的问题:它的行为是填满所有可用的 RAM

使用软键只允许垃圾收集器在没有其他对象引用它们时从缓存中删除对象(即,当唯一引用缓存键的东西是缓存本身时)。它不保证任何其他类型的驱逐。

您找到的大多数解决方案都是在 java Map 类之上添加的功能,包括 EhCache。

你看过commons-collections LRUMap 吗?

请注意, MapMaker 提供 LRU/MRU 功能存在一个未解决的问题。也许你也可以在那里发表你的意见

于 2010-01-28T23:56:45.093 回答
0

使用您现有的缓存,存储 WeakReference 而不是普通的对象引用。

如果 GC 开始耗尽可用空间,WeakReferences 持有的值将被释放。

于 2010-01-29T00:03:22.623 回答
0

过去我使用过JCS。您可以设置配置以尝试满足您的需求。我不确定这是否能满足您的所有要求/需要,但我发现它在我使用时非常强大。

于 2010-01-29T00:50:20.337 回答
0

你不能“删除元素”,你只能停下来硬引用它们并等待 GC 清理它们,所以继续使用 Google Collections ...

于 2010-01-29T07:05:32.037 回答
0

我不知道在 Java 中找出对象大小的简单方法。因此,我认为您不会找到一种方法来限制数据结构所占用的 RAM 数量。

基于这个假设,您只能通过缓存对象的数量来限制它。我建议运行一些实际使用场景的模拟,并收集有关进入缓存的对象类型的统计信息。然后,您可以计算统计平均大小,以及您可以缓存的对象数量。即使它只是您想要专用于缓存的 RAM 量的近似值,它也可能足够好。

至于缓存实现,在我的项目(一个性能关键的应用程序)中,我们使用的是 EhCache,我个人认为它根本不是重量级的。

在任何情况下,使用几种不同的配置(关于大小、驱逐策略等)运行多个测试,并找出最适合您的。

于 2010-01-29T07:22:16.860 回答
0

缓存一些东西,SoftReference也许是迄今为止我能想象的最好的方法。

或者你可以重新发明一个对象池。你不使用的每一个对象,你都不需要销毁它。但它是为了节省 CPU 而不是节省内存

于 2012-03-23T03:55:07.980 回答
-3

假设您希望缓存是线程安全的,那么您应该检查 Brian Goetz 的“Java Concurrency in Practice”一书中的缓存示例。我不能高度推荐这个。

于 2010-01-29T00:21:27.360 回答