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

我们正在用
Java运行RT系统.它通常使用相对较大的堆(100 GB)并处理来自消息队列的请求.必须快速处理每个请求(<100ms)以满足SLA. 我们遇到了严重的GC相关问题,因为GC经常发生请求(200 ms)期间停止收集世界,导致失败. 我们的一位具有合理GC知识的开发人员花了很长时间来调整GC参数并尝试不同的GC.几天后,他想出了一些我们开玩笑称之为“由遗传算法进化而来”的参数化.它可以降低GC暂停时间,但仍远远不能满足SLA要求. 我正在寻找的解决方案是保护GC的一些关键代码部分,并在请求完成后,让GC在下一次请求之前完成所需的工作量.在请求之外偶尔暂停是可以的,因为我们有几个工人,垃圾收集工作人员不会在一段时间内请求请求. 我有一些愚蠢,丑陋,很可能不起作用的想法,但希望他们能说明问题:

>偶尔在接收线程中调用Thread.sleep(),同时祈祷GC做一些工作,

>在请求之间调用System.gc()或Runtime.gc(),再次无可救药地祈求它帮助,

>使用像
https://stackoverflow.com/a/6915221/1137187这样的hacky模式完全复制代码.

最后一个重要的注意事项是,我们是一个低预算的初创公司,商业解决方案,如Zing®不是我们的选择,我们正在寻找一个非商业解决方案.

有任何想法吗?我们将代码完全重写为C(我们不知道GC可能是一个问题而不是开头的解决方案),但代码库已经太大了.

最佳答案

Any ideas?

使用不同的JVM? Azul声称能够处理此类案件. Redhat和Oracle分别为openjdk贡献了shenandoah和zgc,并考虑了类似的目标,所以如果你不想要商业解决方案,也许可以尝试实验版本.

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

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

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

Invoke System.gc() or Runtime.gc() between requests, again hopelessly praying for it to help,

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

点赞