Java 堆大小因 Infinispan 缓存而变得太大

Java heap size growing too big with Infinispan cache

我正在使用 Infinispan 缓存来存储值。代码每 10 分钟写入一次缓存,缓存达到大约 400mb 的大小。 它的生存时间约为 2 小时,最大条目数为 1600 万,尽管目前在我的测试中条目数不超过 200 万左右(我可以通过检查 mbeans/metrics 中的jconsole).

当我启动 jboss 时,java 堆大小为 1.5Gb 到 2Gb。分配给 jboss 的最大内存的 -Xmx 设置为 4Gb。

当我禁用 Infinispan 缓存时,堆内存使用率保持在 1.5Gb 到 2Gb 左右。它非常稳定并保持在那个水平。

=> 问题是:当我有 Infinispan 缓存 enabled 时,java 堆大小增长到大约 3.5Gb/4Gb,这超出了预期。 我做了一个堆转储来检查 Eclipse MAT 中缓存的大小,它只有 300 或 400mb(没问题)。 所以我希望内存使用量达到 2.5Gb 并保持稳定在该水平,因为初始堆大小为 2Gb,最大缓存大小应该仅为 500mb 左右。

然而,随着时间的推移,它会继续增长。每 2 或 3 小时进行一次垃圾收集,使使用量下降到大约 1 或 1.5Gb,但随后在 30 分钟内再次增加到 3.5Gb。 条目数稳定在 200 万左右,所以这并不是因为更多的条目进入缓存。 (驱逐次数也保持为 0)。

如果缓存只有 400-500mb,那么这个内存量还能保持多少? 是我的垃圾回收设置有问题吗?或者我应该看看 Infinispan 设置?

谢谢!

编辑:您可以在此处查看随时间变化的堆大小。 奇怪的是,即使在看起来像是完整 GC 之后,内存也会再次飙升至 3Gb。这对应于更多条目进入缓存。

编辑:事实证明这与 Infinispan 无关。我将问题缩小到使用大量内存的单行代码(比没有调用多大约 1Gb)。 但我确实认为 Infinispan 缓存正在占用越来越多的内存,这自然是因为在 2 小时内添加了更多条目。

我还需要有超过 50 个用户在 Infinispan 上查询。当堆达到这样的高值时(即使没有上面提到的内存泄漏),我知道这不是 java 中的错误情况,但是我需要尽可能多的可用内存。 有什么方法可以 "encourage" 堆转储超过某个点吗?我曾尝试使用 GC 选项以给定比例的堆收集旧代,但通常堆使用量会逐渐增加。

您可能看到的是 JVM 没有收集已从缓存中逐出的对象。缓存通常与分代 GC 的流行思想有着奇怪的关系。

分代 GC 的思想是,从广义上讲,JVM 中有两种类型的对象 - 寿命较短的对象,会很快被使用和丢弃,而寿命较长的对象,通常会在整个生命周期内使用应用程序。在此模型中,您希望调整 GC,以便将大部分精力用于识别短期对象。这意味着您尽可能避免查看长寿命对象。

缓存通过具有一些中等长度的对象生命周期(即几秒/分钟/小时,取决于您的缓存)来破坏这种模式。这些对象通常会被提升到永久代,在那里它们通常不会被查看,直到有必要进行完整的 GC,即使它们已从缓存中被逐出。

如果是这种情况,那么您有两个选择:

  • 忽略它,让完整的 GC 语义发挥它的作用,只需注意这就是正在发生的事情。
  • 尝试调整 GC,使对象需要更长的时间才能提升到永久代。有一些 GC flags 可以帮助解决这个问题。