【问题标题】:Getting an advance warning before full GC在完全 GC 之前获得预先警告
【发布时间】:2013-04-29 07:55:11
【问题描述】:

对于不应暂停超过 200 毫秒的软实时系统的上下文,我们正在寻找一种在 Full GC 迫在眉睫之前发出预警的方法。我们意识到我们可能无法避免它,但我们希望在系统停止之前故障转移到另一个节点。

我们已经能够提出一个方案,在即将到来的完全 GC 之前为我们提供预先警告,这可能会导致系统停顿几秒钟(我们需要避免这种情况)。

我们能够得出的结果依赖于 CMS 空闲列表统计数据:-XX:PrintFLSStatistics=1。这会在每个 GC 周期(包括年轻 GC)之后将空闲列表统计信息打印到 GC 日志中,因此该信息在很短的时间间隔内可用,并且在内存分配率较高的时间间隔内会更频繁地出现。就性能而言,它可能会花费一些成本,但我们的工作假设是我们可以负担得起。

日志的输出如下所示:

Statistics for BinaryTreeDictionary:
------------------------------------
Total Free Space: 382153298
Max   Chunk Size: 382064598
Number of Blocks: 28
Av.  Block  Size: 13648332
Tree      Height: 8

特别是,最大空闲块大小为 382064598 个字。对于 64 位字,这应该刚好低于 2915MB。这个数字一直在缓慢下降,大约每小时 1MB。

我们的理解是,只要最大空闲块大小大于年轻代(假设没有巨大的对象分配),每个对象提升都应该成功。

最近,我们进行了为期数天的压力测试,发现 CMS 能够将最大块大小保持在旧区域总空间的 94% 以上。最大空闲块大小似乎以小于 1MB/小时的速度减少,这应该没问题——据此我们不会很快达到完全 GC,并且服务器可能会因维护而停机比完全 GC 发生的频率更高。

在之前的测试中,在系统内存效率较低的时候,我们已经能够运行系统长达 10 个小时。在第一个小时内,最大空闲块大小已减少到 100MB,并保持了 8 多个小时。在运行的最后 40 分钟内,最大空闲块大小以稳定的速度减少到 0,此时发生了完整的 GC——这非常令人鼓舞,因为对于那个工作负载,我们似乎能够提前 40 分钟警告(当块大小开始稳步下降到 0 时)。

我的问题:假设这一切都反映了长时间的峰值工作负载(生产中任何给定时间点的工作负载只会更低),这听起来像是一种有效的方法吗?您认为我们应该能够从 GC 日志中获得最大空闲块大小统计信息的可靠程度如何?

我们绝对愿意接受建议,但要求它们仅限于 HotSpot 上可用的解决方案(至少目前对我们来说没有 Azul)。此外,G1 本身并不是解决方案,除非我们能提出一个类似的指标,在​​ Full GC 或任何显着超过我们 SLA 的 GC(这些情况偶尔会发生)之前给我们提前警告。

【问题讨论】:

  • 你可以测试JRockit Deterministic GC吗? docs.oracle.com/cd/E15289_01/doc.40/e15071/intro.htm#i1010645
  • 我们知道这一点,以及 IBM 和 Oracle 的其他实时产品。对我们来说,重要的是要有一些较弱的保证(甚至只是启发式),使我们也能够在 HotSpot 上进行部署。
  • 您是否考虑过在交替节点上强制定期进行完整 GC?这将允许更可预测的行为。随着碎片化的增加,CMS 在长期内是不可预测的。
  • @fglez 这似乎是我们最好的选择,但我们希望我们可以监控的指标在故障转移和启动 GC 之前低于临界阈值,而不是定期执行此操作。最大空闲块大小(可通过 GC 日志获得,如上所示)似乎是完美的匹配,因为它会随着碎片的增加而减小。我们正在寻找对此的反馈。这有点不正统,所以即使是“我熟悉 CMS,这对我来说似乎合乎逻辑”这样的评论也会有所帮助:-)
  • @danLeon Concurrent Mark & Sweep,一个用于 Sun/Oracle 的 HotSpot JVM 的低延迟垃圾收集器。

标签: java performance garbage-collection real-time


【解决方案1】:

我在这里发布了来自 Oracle 的 Jon Masamitsu 的一个非常有启发性和令人鼓舞的答案的相关摘录,我从 HotSpot GC 邮件列表 (hotspot-gc-use@openjdk.java.net) 获得该答案——他在 HotSpot 工作,所以这确实是个好消息。

无论如何,这个问题现在仍然悬而未决(我不能相信自己引用了一封电子邮件:-)),所以请添加您的建议!

格式:原始帖子中的引用比 Jon 的回复缩进更多。

我们的理解是,只要最大空闲块大小为 比年轻一代大(假设没有巨大的对象 分配),每个对象提升都应该成功。

在很大程度上这是正确的。有以下情况 从年轻代提升到 CMS 代的对象将需要 CMS 一代比年轻一代有更多的空间。我不 认为这在很大程度上发生了。

以上内容非常令人鼓舞,因为我们绝对可以投入一些备用内存来防止他描述的罕见情况,而且听起来我们会做得很好。

我的问题:假设这一切都反映了一个长期的高峰 工作量(生产中任何给定时间点的工作量只会 更低),这听起来像是一种有效的方法吗?到什么程度 可靠性你认为我们应该能够指望最大 从 GC 日志中释放块大小统计信息?

最大空闲块大小在 GC 打印它时是准确的,但它 在您阅读并做出决定时可能已经过时了。

对于我们的工作负载,这个指标非常缓慢呈螺旋式下降,所以一点陈旧不会对我们造成伤害。

我们绝对愿意接受建议,但要求他们 仅限于 HotSpot 上可用的解决方案(至少对我们来说没有 Azul 目前)。此外,G1 本身不是解决方案,除非我们能想出 一个类似的指标会在 Full GC 之前给我们提前警告,或者 任何显着超过我们 SLA 的 GC(这些有时可能 发生)。

我认为使用最大空闲块大小作为指标是一个很好的指标 选择。它非常保守(听起来像你想要的)并且 不受物体大小的奇怪混合影响。

对于 G1,我认为您可以使用完全免费区域的数量。 我不知道它当前是否打印在任何日志中,但它是 可能是我们维护(或很容易)的指标。如果人数完全免费 区域随着时间的推移而减少,这可能预示着即将到来的完整 GC。

乔恩

谢谢乔恩!

【讨论】:

    【解决方案2】:

    分而治之!

    您的系统使用大量内存,并且需要高响应性。所以重新设计你的系统架构,实现展位。

    识别关键的实时任务并使用他们的业务规则为其创建一个 java 流程。并在其上使用了任何非常规的编程实践,其想法是不依赖 GC 来保持内存清洁。想一想,发挥创造力。

    现在创建其他层和进程,处理其余部分,并构建管道代码以连接所有内容。

    甚至您也可以安排实时进程的生命周期,或检查它们的响应时间,以杀死它并创建一个新的新进程。但我可以预期,你不需要杀死它,以保持它的高响应。

    祝你好运!

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2016-11-08
      • 1970-01-01
      • 2013-10-17
      • 1970-01-01
      • 2019-08-21
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多