在生产环境中设置 -XX:+禁用ExplicitGC:可能出错的地方是什么?

2022-09-01 00:10:30

我们刚刚举行了一次会议,以解决用于计算保险费率的Web应用程序中的一些性能问题。计算在C/C++模块中实现,该模块也用于其他软件包。为了使其可用作Web服务,实现了一个Java包装器,该包装器公开了基于XML的接口并通过JNI调用C / C++模块。

测量表明,在Java部分内部的每个计算上花费了几秒钟。因此,我的第一个重新调整是在 VM 中启用垃圾回收日志记录。我们可以立刻看到,制作了许多停止世界的完整指导性案例。谈到这一点,Java部分的开发人员告诉我们,他们多次做了一个“以确保使用后释放内存”。System.gc()

好吧,我不会再详细阐述这句话了... ;-)

然后,我们还添加了上面提到的 VM 参数并重新运行测试。每次计算大约需要 5 秒。-XX:+DisableExplicitGC

由于我们无法通过在发布过程中的此时剥离所有这些调用来更改代码,因此我们正在考虑在生产中添加内容,直到可以创建新的Jar为止。System.gc()-XX:+DisableExplicitGC

现在的问题是:这样做会有什么风险吗?我唯一能想到的就是在重新部署时在内部使用tomcat,但这只是一个猜测。前面还有其他危险吗?System.gc()


答案 1

您并不是唯一一个通过设置标志来修复停止世界 GC 事件的人。不幸的是(尽管文档中有免责声明),许多开发人员认为他们比JVM更了解何时收集内存并引入这种类型的问题。-XX:+DisableExplicitGC

我知道在很多情况下,生产环境得到了改善,而零个实例存在任何负面的副作用。-XX:+DisableExplicitGC

安全的做法是在负载下运行当前的生产代码,并在压力测试环境中设置该标志,并执行正常的 QA 周期。

如果你不能做到这一点,我建议在大多数情况下设置标志的风险低于不设置标志的成本。


答案 2

我一直在努力解决同样的问题,根据我能够找到的所有信息,肯定存在一些风险。根据@millimoose的原始帖子的评论以及 https://bugs.openjdk.java.net/browse/JDK-6200079,如果使用NIO直接缓冲区,则设置-XX:+DisableExplicitGC似乎是一个坏主意。它们似乎正在我们正在使用的Websphere 8.5应用程序服务器的内部实现中使用。以下是我在调试此内容时能够捕获的堆栈跟踪:

3XMTHREADINFO      "WebContainer : 25" J9VMThread:0x0000000006FC5D00, j9thread_t:0x00007F60E41753E0, java/lang/Thread:0x000000060B735590, state:R, prio=5
3XMJAVALTHREAD            (java/lang/Thread getId:0xFE, isDaemon:true)
3XMTHREADINFO1            (native thread ID:0x1039, native priority:0x5, native policy:UNKNOWN)
3XMTHREADINFO2            (native stack address range from:0x00007F6067621000, to:0x00007F6067662000, size:0x41000)
3XMCPUTIME               CPU usage total: 80.222215853 secs
3XMHEAPALLOC             Heap bytes allocated since last GC cycle=1594568 (0x1854C8)
3XMTHREADINFO3           Java callstack:
4XESTACKTRACE                at java/lang/System.gc(System.java:329)
4XESTACKTRACE                at java/nio/Bits.syncReserveMemory(Bits.java:721)
5XESTACKTRACE                   (entered lock: java/nio/Bits@0x000000060000B690, entry count: 1)
4XESTACKTRACE                at java/nio/Bits.reserveMemory(Bits.java:766(Compiled Code))
4XESTACKTRACE                at java/nio/DirectByteBuffer.<init>(DirectByteBuffer.java:123(Compiled Code))
4XESTACKTRACE                at java/nio/ByteBuffer.allocateDirect(ByteBuffer.java:306(Compiled Code))
4XESTACKTRACE                at com/ibm/ws/buffermgmt/impl/WsByteBufferPoolManagerImpl.allocateBufferDirect(WsByteBufferPoolManagerImpl.java:706(Compiled Code))
4XESTACKTRACE                at com/ibm/ws/buffermgmt/impl/WsByteBufferPoolManagerImpl.allocateCommon(WsByteBufferPoolManagerImpl.java:612(Compiled Code))
4XESTACKTRACE                at com/ibm/ws/buffermgmt/impl/WsByteBufferPoolManagerImpl.allocateDirect(WsByteBufferPoolManagerImpl.java:527(Compiled Code))
4XESTACKTRACE                at com/ibm/io/async/ResultHandler.runEventProcessingLoop(ResultHandler.java:507(Compiled Code))
4XESTACKTRACE                at com/ibm/io/async/ResultHandler$2.run(ResultHandler.java:905(Compiled Code))
4XESTACKTRACE                at com/ibm/ws/util/ThreadPool$Worker.run(ThreadPool.java:1864(Compiled Code))
3XMTHREADINFO3           Native callstack:
4XENATIVESTACK               (0x00007F61083DD122 [libj9prt26.so+0x13122])
4XENATIVESTACK               (0x00007F61083EA79F [libj9prt26.so+0x2079f])
....

当使用NIO直接字节缓冲区时,设置-XX:+DisableExplicitGC的确切后果对我来说还不完全清楚(这是否会导致内存泄漏?),但至少似乎存在一些风险。如果您使用的是 Websphere 以外的应用程序服务器,则在禁用应用程序服务器之前,您可能需要验证应用程序服务器本身是否未通过 NIO 调用 System.gc()。我有一个相关的问题,希望能在这里得到一些关于对NIO库的确切影响的澄清:当使用NIO直接缓冲区时,设置-XX:+DisableExplicitGC的影响

顺便说一句,Websphere似乎在启动过程中也多次手动调用System.gc(),通常在应用程序服务器启动后的前几秒钟内调用两次,在前1-2分钟内(可能是在部署应用程序时)调用第三次。在我们的例子中,这就是我们首先开始调查的原因,因为似乎所有的System.gc()调用都直接来自应用程序服务器,而不是来自我们的应用程序代码。

还应该注意的是,除了NIO库之外,RMI分布式垃圾回收的JDK内部实现还调用System.gc():由于核心API的远程方法调用System.gc()调用,无法解释的系统.gc()调用。

启用 -XX:+DisableExplicitGC 是否也会对 RMI DGC 造成严重破坏,我也有点不清楚。我能找到的唯一一个甚至解决这个问题的参考文献是上面的第一个参考文献,它指出

“然而,在大多数情况下,常规的GC活性足以实现有效的DGC”

“在大多数情况下”这个限定词对我来说听起来非常一厢情愿,所以再次,似乎至少有一些风险只是关闭所有System.gc()调用,如果可能的话,你最好修复代码中的调用,并且只是将它们完全关闭作为最后的手段。