在生产环境中设置-XX:+DisableExplicitGC会有哪些问题?

48
我们刚刚开了一次会,讨论了一个用于计算保险费率的Web应用程序的性能问题。这些计算是在一个C/C++模块中实现的,该模块也被其他软件包使用。为了将其作为Web服务提供,实现了一个Java封装器,它公开了一个基于XML的接口,并通过JNI调用C/C++模块。
测量结果表明,在Java部分的每个计算中有几秒钟的时间都被消耗了。因此,我的第一个建议是在VM中启用垃圾回收日志记录。我们可以立即看到许多全停顿的Full GCs被执行。谈到这一点,Java部分的开发人员告诉我们,他们在几个场合下执行了System.gc()“以确保内存在使用后被释放”。
好吧,我不会进一步解释那个语句...... ;-)
然后我们添加了上述的-XX:+DisableExplicitGC到VM的参数中,重新运行了测试。这样每个计算节约了约5秒钟的时间。
由于在我们的发布流程中无法通过剥离所有这些System.gc()调用来更改代码,所以我们考虑在生产环境中添加-XX:+DisableExplicitGC,直到创建一个新的JAR文件为止。
现在的问题是:这样做会有任何风险吗?我唯一能想到的就是当重新部署时Tomcat会在内部使用System.gc(),但这只是一个猜测。还有其他什么潜在的危害吗?

5
由于System.gc本身没有任何保证,因此从技术上讲,您不会破坏任何东西,但可能会触发一些已经存在问题的代码中的bug。然而,这并没有什么安慰作用。 - Marko Topolnik
8
当然它有作用,这就是它存在的原因 :) 但是,即使它没有任何作用,它仍然会遵守其契约。因此,在“-XX:+DisableExplicitGC”下出现故障的任何代码都是有问题的。 - Marko Topolnik
4
再次证明,几乎不应该尝试预测垃圾回收,并自己调用System.gc() :) - matt b
1
@Axel:就我所知,你很可能不知道自己在使用NIO直接缓冲区。它们似乎是一种IO优化方式,用于处理大型、长期存在的字节缓冲区,这听起来不像是业务Web应用程序中会遇到的问题。 - millimoose
2
@millimoose 大多数现代缓存实现使用直接缓冲区进行堆外存储。但我猜测缓存实现不会对堆的常规操作产生干扰。 - Marko Topolnik
显示剩余6条评论
3个回答

44

在解决“stop-the-world”垃圾回收事件时,通过设置-XX:+DisableExplicitGC标志来解决问题的人并不孤单。不幸的是(尽管文档中有免责声明),许多开发者认为自己比JVM更懂何时回收内存,并因此引入了这种类型的问题。

我知道许多情况下使用-XX:+DisableExplicitGC可以改善生产环境,并且没有任何负面影响的实例。

安全起见,请在压力测试环境下运行当前的生产代码并设置该标志,在正常的QA周期内进行测试。

如果您无法执行上述操作,我建议在大多数情况下,设置该标志的风险要小于不设置的成本。


2
关于这一点:“许多开发人员认为自己比JVM更懂”。实际上,这是Java文档的问题。System.gc的描述并没有说明它是一个危险的命令。我看到了太多的教程建议经常使用System.gc来释放内存。https://docs.oracle.com/javase/7/docs/api/java/lang/System.html#gc() - Nux

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])
....

设置-XX:+DisableExplicitGC时,当使用NIO直接字节缓冲区时的全部影响还不是很清楚(这会引入内存泄漏吗?),但至少存在一些风险。如果您使用的应用程序服务器不是Websphere,则可能需要验证应用程序服务器本身是否通过NIO调用System.gc(),然后再禁用它。我有一个相关问题,希望能够在此处对NIO库的确切影响进行澄清:设置-XX:+DisableExplicitGC时,当使用NIO直接缓冲区时的影响 顺便说一下,Websphere似乎在启动过程中手动调用System.gc()多次,通常在应用程序服务器启动后的前几秒钟内两次,在第1-2分钟内第三次(可能在部署应用程序时)。在我们的情况下,这就是为什么我们首先开始调查的原因,因为所有System.gc()调用都是直接来自应用程序服务器,从未来自我们的应用程序代码。
还应该注意的是,除了NIO库之外,JDK内部实现的RMI分布式垃圾收集也调用System.gc():由于远程方法调用而导致无法解释的System.gc()调用 核心API的System.gc()调用 至于启用-XX:+DisableExplicitGC是否也会对RMI DGC造成破坏,这也不太清楚。我能找到的唯一涉及此问题的参考资料是上面提到的第一个参考资料,其中指出:
“然而,在大多数情况下,定期进行GC活动就足以实现有效的DGC。”
这个“在大多数情况下”的限定词听起来很含糊,因此,如果可能的话,最好修复代码中的调用,并仅在最后一步完全关闭它们。

1
如果您在RMI客户端中使用-XX:+ DisableExplicitGC,由于此错误(https://bugs.openjdk.java.net/browse/JDK-6791811),您可能会导致RMI服务器上的OOM。 - Anton Koscejev

1
如果您使用-XX:+DisableExplicitGC并使用CMS,则可能还想使用-XX:+CMSClassUnloadingEnabled来限制全GC的另一个原因(即PermGen已满)。除此之外,我使用该选项没有遇到问题,尽管我已经切换到使用-XX:+ExplicitGCInvokesConcurrentAndUnloadsClasses,因为我的显式GC的唯一原因是RMI而不是应用程序代码。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接