【问题标题】:Any Java caches that can limit memory usage of in-memory cache, not just instance count?任何可以限制内存缓存的内存使用的Java缓存,而不仅仅是实例计数?
【发布时间】:2010-10-16 00:05:13
【问题描述】:

我正在寻找一个简单的内存(和进程内)缓存,用于查询数据的短期缓存(但短期含义超出请求/响应,即会话边界)。 EhCache 可能会起作用,但它看起来好像不能提供我需要的一件事:不是对缓存对象数量的限制,而是对缓存数据消耗的内存量的(近似)限制。

我知道在没有序列化的情况下很难确定给定对象的确切内存使用情况(在一般情况下我想避免这种情况,因为它的速度很慢违背了我的使用目的),我可以不必提供大小估计自己。

那么:是否有一个简单的开源 java 缓存允许定义缓存对象的“权重”,以限制缓存的内容数量?

编辑(2010 年 11 月):值得一提的是,有一个名为 Java CacheMate 的新项目试图解决这个问题,以及其他一些改进想法(多级内存进程内缓存)

【问题讨论】:

  • 嗯。我的问题一定写得很糟糕,因为大多数答案都集中在对我来说没有问题的部分。我可以提供有用的估计——我只需要缓存来使用它。也就是说,不仅仅是物体的数量,还有总重量。我想没有一个“标准”的这样做。
  • 我认为您需要的更多细节会有所帮助。您想要考虑的唯一因素是物品的(静态)重量吗?大概你想要一个缓存可以支持的总“重量”......时间呢?这应该发挥作用吗?
  • 请问为什么需要基于大小的缓存?
  • 是的:只是静态重量。我知道有许多有用且有趣的因素:我可以在这里忽略它们。新鲜度可能很重要,但 LRU 在这里也很好。并且基于大小是为了限制堆缓存的使用量。感谢所有的答案,cmets,希望这会有所帮助!
  • 至于为什么基于大小:确保缓存和内容的内存使用是有界的(在适当近似的范围内)——我可能想为一个缓存分配大约 100 兆,为另一个缓存分配 200 ,总的最大堆为 1 gig。

标签: java caching memory-management ehcache


【解决方案1】:

我同意 Paul 的观点,这通常可以通过使用软引用缓存来解决,尽管它可能会比您希望的更早驱逐条目。一个通常可以接受的解决方案是使用一个普通的缓存,它驱逐到软缓存,并在可能的情况下恢复未命中的条目。这种受害者缓存方法效果很好,可以降低门槛,但如果可用内存可用,则会带来额外的好处。

可以通过启用 Java 代理来确定内存大小,使用 SizeOf 实用程序 (http://sourceforge.net/projects/sizeof) 时使用非常简单。我仅将其用于调试目的,我建议在将其用于正常使用之前对开销进行基准测试。

在我的缓存库中,我计划在实现核心算法后添加插入评估器的功能。这样,您可以将集合存储为值,但通过所有集合大小的总和来绑定缓存。我已经看到无限集合作为缓存中的值会导致 OutOfMemoryExceptions,因此控制非常方便。

如果你真的需要这个,我建议不要这样做,我们可以增强我当前的实现来支持这个。你可以给我发电子邮件,ben.manes-at-gmail.com。

【讨论】:

  • 这实际上听起来像是添加后可以工作的东西。就像我说的,我不需要确切的用法;我可以很容易地计算出 byte[]s 等的近似成本。我只需要一个可以使用权重的缓存。
  • 请给我发电子邮件,以便我们进一步讨论。我相信你可以毫不费力地为此装饰一个 LinkedHashMap。这样做可以解决我必须解决的种族问题,因为我没有使用巨型锁来保护整个结构。所以我宁愿暂时关闭这个功能......
【解决方案2】:

如何使用启用 LRU 算法的简单 LinkedHashMap 并将所有带有 SoftReference 的数据放入其中...例如 cache.out(key, new SoftReference(value)) ??

这会将您的缓存限制为可用内存量,但不会杀死程序的其余部分,因为当有内存需求时,Java 会删除软引用......不是全部......通常是最旧的第一个......如果您将引用队列添加到您的实现中,您还可以从映射中删除停顿条目(只有键,没有值)。

这将使您免于计算条目的大小并跟踪总和。

【讨论】:

  • 将软引用用于缓存实现并不是一个好主意。至少不在应用服务器中。原因是你不能很好地控制他们的寿命。
  • 嗯,这取决于您的需求。通常缓存只需要有限的可预测性。在内存不足的情况下,由紧急值支持的 LRU 算法还不错。您可以使用两级缓存来改进它...首先是硬引用,一段时间后是软引用。
  • 重点是Softreferences的存活时间只取决于app server的空闲内存。您无法根据应用程序独立控制它。
  • 为什么要控制它?这是很好的部分,我可以使用我拥有的内存进行缓存(因此是缓存这个词),如果我没有内存......嘿,我必须重新计算。
  • 是的,如果您有一个可以工作的单用户桌面应用程序。但是在应用服务器上,您最终可能会遇到多个应用程序争夺可用内存,并且您无法控制每个应用程序获得多少。
【解决方案3】:

EhCache V2.5 目前提供了一种解决方案,可以根据缓存的内存大小设置上限。更多详情请查看EhCache 2.5 Documentation

【讨论】:

  • 是的,我看到了,这是一个有用的补充。
【解决方案4】:

这不仅难以衡量 - 还难以定义。

假设两个缓存条目引用同一个字符串 - 它们是否计算该字符串的大小,尽管从缓存中删除它们中的任何一个都不会使字符串符合垃圾的条件收藏?尽管如果 both 从缓存中删除,那么它们都没有计算大小,那么字符串可能有资格被收集?如果不在缓存中的另一个对象引用了该字符串怎么办?

如果您可以准确地描述您感兴趣的尺寸可能可以通过编程方式确定它 - 但我怀疑您会发现甚至很难确定您想要什么。 p>

【讨论】:

  • 嗨,乔恩,您的评论总体上说得通,但是有一种有意义的方法可以计算缓存的大小(保留大小)。请参阅下面的答案。
  • 我对通用解决方案不感兴趣——对于我的用例,是的,我可以很容易地确定近似大小。我同意,在一般情况下,这当然是不可能的。没关系,我不需要解决这个问题。 :)
  • 好的,所以你想要按大小加权。您是否还想要上次使用的权重?你希望它们如何平衡?如果没有 last time 元素,您可以使用 PriorityQueue 和 HashMap 来确定要驱逐的内容。保留一个总数,以便您知道何时驱逐。
  • 对我来说,驱逐什么的优先级和缓存的总边界仍然是单独的问题; LRU 很好,即使这意味着一个大项目要消耗整个缓存“配额”。所以是的,我同意,保持单独的排序和队列是有意义的。谢谢!
【解决方案5】:

除了猜测对象的内存使用情况外,对于一个合理的算法,您还需要猜测重新创建它的成本。一个合理的猜测是娱乐成本大致与内存大小成正比。所以这些因素相互抵消,你不需要。一个简单的算法可能会更好。

【讨论】:

    【解决方案6】:

    如果您无法做出任何估计 - 编写一个缓存逐出策略,该策略基于 JVM 堆大小(从系统轮询)或由来自孤立对象的 finalize() 调用触发(在 GC 上)。

    【讨论】:

      【解决方案7】:

      可以为缓存的内存使用定义一个有意义的度量。您可以计算:"retained size"。 不幸的是,计算保留大小的成本大致与完整 GC 一样高,因此可能不是一种选择。在某些 JVM 语言(clojure?)中,理论上您可以确保缓存中的任何对象都不会被外部对象引用,然后您可以监控缓存的实际大小。

      【讨论】:

      • 但是,该措施没有考虑到将两个条目中的任何一个从缓存中推出可能几乎没有空间释放,但将 both 推出可能会释放这一事实数量很大。
      • 嗨 Jon,您只需要计算整个缓存的“保留集”。您模拟缓存“根”对象(现在假设它是一个),可以从内存中删除,运行模拟的完整 GC,然后计算哪些对象将被回收。
      【解决方案8】:

      完成这项工作的是 java.lang.ref.SoftReference 。通常,您扩展 SoftReference 类,以便子类包含键。

      【讨论】:

        猜你喜欢
        • 2010-09-26
        • 2012-04-23
        • 2013-05-22
        • 2011-11-15
        • 2015-11-09
        • 1970-01-01
        • 1970-01-01
        • 2010-11-23
        • 2021-01-23
        相关资源
        最近更新 更多