【发布时间】:2016-10-26 20:39:20
【问题描述】:
我们正在用 Java 运行一个 RT 系统。它通常使用相对较大的堆(100+GB)并为来自消息队列的请求提供服务。必须快速处理每个请求(
我们遇到了严重的 GC 相关问题,因为经常发生 GC 在请求期间(200+ms)导致 stop-the-world 收集,从而导致失败。
我们的一位对 GC 有一定了解的开发人员花了很多时间调整 GC 参数并尝试不同的 GC。几天后,他提出了一些我们戏称“由遗传算法进化”的参数化。它降低了 GC 暂停,但仍远未满足 SLA 要求。
我正在寻找的解决方案是保护代码的一些关键部分免受 GC,在请求完成后,让 GC 完成所需的工作,在采取之前下一个请求。请求之外的偶尔暂停是可以的,因为我们有几个工作人员,而垃圾收集工作人员暂时不会请求请求。
我有一些想法很愚蠢,很丑陋,而且很可能不起作用,但希望它们能说明问题:
- 偶尔在接收线程中调用
Thread.sleep(),祈求GC同时做一些工作, - 在请求之间调用
System.gc()或Runtime.gc(),再次绝望地祈祷它提供帮助, - 用https://stackoverflow.com/a/6915221/1137187 之类的hacky 模式完全弄乱代码。
最后重要的一点是,我们是一家低预算的初创公司,Zing® 等商业解决方案不适合我们,我们正在寻找非商业解决方案。
有什么想法吗?我们会将我们的代码完全重写为 C++(我们一开始并不知道 GC 可能是一个问题而不是解决方案),但是代码库已经太大了,无法做到这一点。
【问题讨论】:
-
当我听到“实时”这个词时,Java 肯定不是我想到的第一种语言,鉴于选择了 Java,对巨大堆的需求似乎不是好兆头。
-
无论如何,对于长时间运行的进程中的 GC 问题,实际上只有两种通用方法:(1) 减少产生的垃圾量,以及 (2) 使垃圾更快地收集。如果完整 GC 成本高但不频繁,那么一种替代方法可能是大大减少堆大小。这将需要更频繁的 GC,但每个 GC 都应该更快,因为不能收集那么多垃圾。此外,尽量避免长生命周期的对象,因为它们对于分代 GC 的收集成本更高,除非它们在进程的整个生命周期中都被保留。
-
另外,注意临时对象。 Java 程序员通过创建和丢弃大量对象比需要更多地依赖 GC 的情况并不少见。他们甚至可能没有意识到他们正在这样做。例如,字符串连接和自动装箱可以在这里做出贡献。基元没有 GC 成本,根据经验,较低级别的 API 产生的垃圾更少。
-
关于保护关键代码免受 GC,Java 没有提供直接适用于该目标的 API。如果是这样,它的使用将引入关键代码将失败并出现
OutOfMemoryError的风险,否则它只会被 GC 延迟。那会造成更大的混乱。 -
@Tregoreg,大
HashMap的图中几乎所有对象都与条目相关联;无论您是否重复使用地图,它们都会变成垃圾。如果这些是导致问题的重要原因,那么想出一种用double[]s 替换它们的方法可能会有所帮助,前提是这样做不需要在其他地方创建与保存的一样多的额外对象地图。
标签: java garbage-collection real-time heap-memory