【问题标题】:How do I efficiently cache objects in Java using available RAM?如何使用可用 RAM 在 Java 中有效地缓存对象?
【发布时间】:2011-01-10 15:50:35
【问题描述】:

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

我的要求是:

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

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

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

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

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

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

【问题讨论】:

  • 你在缓存什么样的对象?我不太关心缓存的性能,考虑到一旦你在过期策略之后,你会比普通的 Map 产生更多的开销,而 EHCache 是一个开发良好的缓存库,它(我'我想在这里通过 Spring 进行配置)设置起来并不复杂,而且使用起来比地图简单。
  • 对象的大小从大约 1kb 到大约 10kbs 不等。我担心性能,因为从缓存中检索对象是在 CPU 密集型进程的内部循环中。如果它很慢,它可能会将我的应用程序执行其操作所需的时间从几分钟增加到几小时。
  • 使用 softKeys() 如果你使用 equals(),你不会得到一个命中,只有当你用引用相等查找对象时你才会得到一个命中。如果您需要 equals() 来进行缓存命中,请改用 softValues()。

标签: java caching guava soft-references


【解决方案1】:

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

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

【讨论】:

  • 我熟悉该文档,但我不知道其中没有任何内容可以解决我的问题 :-(
【解决方案2】:

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

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

【讨论】:

    【解决方案3】:

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

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

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

    【讨论】:

      【解决方案4】:

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

      【讨论】:

        【解决方案5】:

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

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

        【讨论】:

        • 此解决方案与我提到的 LinkedHashMap 存在相同的问题,即(来自 EHCache 文档)“所有缓存在配置时根据元素数量指定其最大内存大小。 "
        • @sanity 我明白了...但是如果您不想用缓存的对象填充 all 可用内存(从而避免 GC),那么我不会不知道如何在不限制缓存中对象数量的情况下做到这一点。您是否进行了一些具有代表性的基准测试来了解不同限制下的内存大小?
        • 如果没有更好的选择,我可能最终会对其进行基准测试,但这让我担心,因为现实可能会以意想不到的方式与我的基准测试不同,从而导致各种令人头疼的问题。
        • @sanity EHCache 现在似乎支持以字节为单位指定缓存大小。来自最近的 EHCache 发布信息(2011 年 11 月 15 日):“调整缓存大小现在就像设置最大字节数一样简单。不再需要设置最大条目数和调整驱逐参数来近似缓存可以使用的最大系统内存量使用。”
        【解决方案6】:

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

        基于这个假设,您只能通过缓存对象的数量来限制它。我建议对一些现实生活中的使用场景进行模拟,并收集有关进入缓存的对象类型的统计信息。然后,您可以计算统计平均大小,以及您可以缓存的对象数量。尽管这只是您希望专用于缓存的 RAM 量的近似值,但它可能已经足够了。

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

        无论如何,使用几种不同的配置(关于大小、驱逐策略等)运行多个测试,然后找出最适合您的。

        【讨论】:

          【解决方案7】:

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

          【讨论】:

            【解决方案8】:

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

            【讨论】:

              【解决方案9】:

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

              【讨论】:

                【解决方案10】:

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

                【讨论】:

                • 这并没有真正解决他的问题。
                • @danben:我同意。但是线程安全是他必须考虑的事情。
                【解决方案11】:

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

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

                【讨论】:

                【解决方案12】:

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

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

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

                你看过commons-collectionsLRUMap吗?

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

                【讨论】:

                • LRUMap 要求您指定地图的最大大小,但我不知道这将是什么 - 我只是希望缓存不断增长以填充其他未使用的 RAM。
                猜你喜欢
                • 2015-07-27
                • 2021-09-24
                • 1970-01-01
                • 1970-01-01
                • 2021-09-03
                • 1970-01-01
                • 1970-01-01
                • 2016-12-22
                • 2016-03-29
                相关资源
                最近更新 更多