如何使用可用RAM有效地在Java中缓存对象?

时间:2010-01-28 23:45:04

标签: java caching guava soft-references

我需要使用一定比例的RAM来缓存Java中的对象。我知道其他人已经提出了这个问题,但没有一个回复符合我的要求。

我的要求是:

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

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

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

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

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

我听说过像EHCache这样的东西,但它似乎对我需要的东西非常重,我不确定它是否足够快我的应用程序(记住解决方案不会比一个HashMap)。

12 个答案:

答案 0 :(得分:7)

我对你有类似的要求 - 并发(在2个hexacore CPU上)和LRU或类似的 - 并且还尝试了Guava MapMaker。我发现softValues()比weakValues()慢得多,但是当内存填满时,两者都让我的app变得极为缓慢。

我尝试了WeakHashMap并且它的问题较少,比通过其removeEldestEntry()方法将LinkedHashMap用作LRU缓存更奇怪。

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

答案 1 :(得分:4)

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

答案 2 :(得分:3)

  

我听说过像EHCache这样的东西,但它似乎对我需要的东西非常重,我不确定它是否足够快我的应用程序(记住解决方案不会比一个HashMap)。

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

答案 3 :(得分:2)

我相信MapMaker将是获得你所要求的唯一合理方式。如果“GC开始捶打并且整个应用程序的性能急剧恶化”,您应该花一些时间正确设置各种调整参数。这个文件起初可能看起来有些令人生畏,但它实际上写得非常清楚,并且是关于GC的有用信息的金矿:

http://java.sun.com/j2se/reference/whitepapers/memorymanagement_whitepaper.pdf

答案 4 :(得分:1)

我不知道这是否是一个简单的解决方案,特别是与EHCache或类似的解决方案,但你看过Javolution library吗?它并非如此设计,但在javolution.context包中,它们具有可以重用对象而无需垃圾回收的Allocator模式。这样,它们可以将对象创建和垃圾收集保持在最低限度,这是实时编程的一个重要特性。也许您应该看看并尝试使其适应您的问题。

答案 5 :(得分:0)

  

这看起来很吸引人   当它自动删除元素   需要更多的RAM,但有一个   严重的问题:它的行为是   填满所有可用内存

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

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

你看过commons-collections LRUMap了吗?

请注意,MapMaker有一个open issue来提供LRU / MRU功能。也许你也可以在那里表达你的意见

答案 6 :(得分:0)

使用现有缓存,存储WeakReference而不是正常的对象引用。

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

答案 7 :(得分:0)

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

答案 8 :(得分:0)

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

答案 9 :(得分:0)

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

基于这个假设,您不得不通过缓存对象的数量来限制它。我建议运行几个真实使用场景的模拟,并收集进入缓存的对象类型的统计信息。然后,您可以计算统计平均大小以及可以承受缓存的对象数。尽管它只是您想要专用于缓存的RAM量的近似值,但它可能已经足够了。

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

在任何情况下,运行几个不同配置的测试(关于大小,驱逐政策等)并找出最适合你的方法。

答案 10 :(得分:0)

缓存某些内容,SoftReference可能是迄今为止我能想象到的最佳方式。

或者你可以重新发明一个对象池。你没有使用的每个物体,你都不需要破坏它。但它节省CPU而不是节省内存

答案 11 :(得分:-3)

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