在实时系统中控制Java垃圾收集

Tre*_*reg 5 java garbage-collection real-time heap-memory

我们正在用 Java 运行一个 RT 系统。它通常使用相对较大的堆(100+GB)并为来自消息队列的请求提供服务。必须快速处理每个请求(<100 毫秒)以满足 SLA。

我们遇到了严重的 GC 相关问题,因为经常发生 GC 在请求期间(200+ms)导致 stop-the-world 收集,从而导致失败。

我们的一位对 GC 有一定了解的开发人员花了相当多的时间来调整 GC 参数并尝试不同的 GC。几天后,他提出了一些我们戏称为“由遗传算法进化”的参数化方法。它降低了 GC 暂停,但仍远未满足 SLA 要求。

我正在寻找的解决方案是保护代码的一些关键部分免受 GC 的影响,并且在请求完成后,让 GC在接受下一个请求之前根据需要做尽可能多的工作。请求之外的偶尔暂停是可以的,因为我们有几个工作人员,垃圾收集工作人员暂时不会请求请求。

我有一些愚蠢、丑陋且很可能不起作用的想法,但希望它们能说明问题:

  • 偶尔调用Thread.sleep()接收线程,祈祷GC在此期间做一些工作,
  • 调用System.gc()Runtime.gc()请求之间,再次绝望地祈祷它提供帮助,
  • 使用像/sf/answers/484065501/这样的黑客模式完全弄乱代码。

最后一个重要说明是,我们是一家低预算的初创公司,Zing®等商业解决方案不适合我们,我们正在寻找非商业解决方案。

有任何想法吗?我们会将我们的代码完全重写为 C++(我们一开始并不知道 GC 可能是一个问题而不是解决方案),但是代码库已经太大而无法这样做。

the*_*472 3

有任何想法吗?

使用不同的 JVM?Azul 声称能够处理此类案件。Redhat 和 Oracle 分别向 openjdk 贡献了 Shenandoah 和 zgc,有着相似的目标,所以如果您不想要商业解决方案,也许您可​​以尝试实验性构建。

还有其他 JVM 专注于实时应用程序,但据我了解,它们专注于较小系统上较硬的实时要求,而您的听起来更像是软实时要求。

您可以尝试的另一件事是通过使用预分配的对象或更紧凑的数据表示(如果适用)来显着减少对象分配(分析您的应用程序!)。在保持新生代大小相同的同时减少分配压力意味着每次收集的死亡率增加,这应该会加速年轻收集的速度。

选择硬件来最大化内存带宽也可能有所帮助。

在请求之间调用 System.gc() 或 Runtime.gc(),再次绝望地祈祷它能提供帮助,

与 结合使用时这可能会-XX:+ExplicitGCInvokesConcurrent起作用,否则它将触发 CMS 或 G1 的单线程 STW 集合(我假设您正在使用其中之一)。但这种方法似乎很脆弱,需要大量的调整和监控。