1

是否有可能在内存中实现缓存以避免完全堆消耗?

我的 spring-boot java 应用程序使用内存中的缓存,并将过期策略设置为 1 小时(Caffeine 库用于缓存目的)。在那之后,所有缓存实例都在老年代,需要收集完整的 GC。现在将 XMX 设置为 10GB,我可以看到经过几个小时的测试,我的缓存包含大约 100k 个实例,但是在堆中(正好在老一代中)我可以找到几百万个缓存对象的实例。有没有可能在内存中使用缓存并避免这种情况?

4

3 回答 3

1

您描述的问题是调用内存泄漏。

是的,您可以,但这取决于您使用的 GC 版本。例如在 G1 中这个问题不应该出现。所以,如果这是可能的,我建议你切换到 G1。

XpauseTarget 这个标志是避免系统长时间停顿的责任,因此您可以将清理堆拆分为部分。

您还可以自定义需要运行 GC 的百分比。-XX:InitiatingHeapOccupancyPercent=45

于 2021-03-12T07:55:28.923 回答
0

正如您所观察到的,缓存和分代收集器具有相反的目标。更现代的收藏家,如 G1 和雪兰多,是基于地区的,这可以让他们更好地处理老一代的收藏。在 Shenandoah 技术讲座中,您经常会听到他们的开发人员讨论将 LRU 缓存作为压力测试。如果您的 GC 调整良好,这可能不是问题。

您可以将缓存数据结构保留在堆上,但将其条目移开。这可以通过以ByteBuffer访问开销为代价将值序列化为 a 来完成。Apache Mnemonic提供了另一种方法,它在堆外存储对象字段并透明地编组数据。这避免了序列化成本,但侵入了对象模型。

有像Oak这样的完全堆外哈希表和像OHC这样的缓存。这些尽可能多地移动到 GC 之外,但与堆上缓存相比,开销要大得多。这与使用远程缓存(如 memcached 或 redis)相当,因此可能更受欢迎。例如,Memcached 使用slab 分配来非常有效地处理内存流失。

大多数情况下,您会看到一个小的堆上缓存用于快速本地访问最常用的数据,这些数据由大型远程缓存支持,用于其他所有操作。如果您确实需要多 GB 的进程内缓存,则可能需要堆外缓存,或者您可能必须调整 GC 以适应此工作负载。

于 2021-03-12T07:57:09.603 回答
0

如果未设置过期,缓存中的对象始终存在。你可以做的是调整 JVM 以避免这种情况,即,如果你使用 CMS,-XX:CMSInitiatingOccupancyFraction=75 -XX:+UseCMSInitiatingOccupancyOnly在设置这两个选项的情况下,当老年代超过 75% 时,JVM 将被迫执行 full gc。

于 2021-03-12T07:59:38.733 回答