当使用NIO直接缓冲区时设置-XX:+DisableExplicitGC的影响。
当使用NIO直接缓冲区时设置-XX:+DisableExplicitGC的影响。
我们正在构建一个具有高性能SLAs的网络应用程序,但由于JVM由于System.gc()调用而暂停,这些SLAs定期被违反。我们已经进行了一些调试,并确定在所有情况下都是内部应用服务器代码调用了System.gc()。这在应用服务器启动或应用程序部署时会发生几次,这并不是我们关心的问题。然而,System.gc()也会定期通过内部应用服务器调用NIO类时触发。以下是我们能够捕获的此事件的堆栈跟踪:\n
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使用总计:80.222215853秒 3XMHEAPALLOC 上次GC周期分配的堆字节数=1594568 (0x1854C8) 3XMTHREADINFO3 Java调用堆栈: 4XESTACKTRACE at java/lang/System.gc(System.java:329) 4XESTACKTRACE at java/nio/Bits.syncReserveMemory(Bits.java:721) 5XESTACKTRACE (进入锁定:java/nio/Bits@0x000000060000B690,入口计数:1) 4XESTACKTRACE at java/nio/Bits.reserveMemory(Bits.java:766(Compiled Code)) 4XESTACKTRACE at java/nio/DirectByteBuffer.(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调用堆栈: 4XENATIVESTACK (0x00007F61083DD122 [libj9prt26.so+0x13122]) 4XENATIVESTACK (0x00007F61083EA79F [libj9prt26.so+0x2079f]) ....
\n如果我们通过启用-XX:+DisableExplicitGC(或者在我们的情况下设置-Xdisableexplicitgc,因为我们在IBM JRE上运行Websphere,它会执行相同的操作)来停止对System.gc()的调用,那么这会有什么影响?我们当然不想创建内存泄漏。我没有找到关于为什么NIO中的System.gc()调用实际上是必要的的直接参考,并且在JDK代码中它发生的地方也没有特定的代码注释: http://hg.openjdk.java.net/jdk8u/jdk8u-dev/jdk/file/4a1e42601d61/src/share/classes/java/nio/Bits.java\n如果由于使用NIO而完全禁用System.gc()是个坏主意,那么我们至少能做些什么来减少其调用的频率吗?似乎我们可以设置-XX:MaxDirectMemorySize,但这似乎只会设置已分配内存的上限,并且很可能会产生不利影响。
当使用NIO直接缓冲区时,设置-XX:+DisableExplicitGC会产生影响。禁用显式GC并不能阻止缓冲区及其所持有的本地内存被回收,但可能会延迟回收很长时间。
这意味着直接缓冲区分配的内存可能会在很长时间内累积,直到被回收。从长远来看,这并不真正是一个内存泄漏,但会增加峰值内存使用量。
根据我理解,System.gc()的调用是为了在达到reserveMemory限制时释放缓冲区。在保留所请求的数量后,ByteBuffer.allocateDirect将调用Unsafe.allocateMemory,如果其尝试的内存映射失败,可能会进行自己的GC调用,而这不应受到DisableExplicitGC的影响。
只有在达到MaxDirectMemorySize限制时才会调用System.gc()。如果您可以调整您的GC或应用程序代码,使其满足以下选项之一:
- 使用固定的缓冲区集(->限制永远不会超过)
- 早期回收缓冲区(短期存活的缓冲区->在Young GC中消失)
- 在直接缓冲区空间耗尽之前定期回收老年代
- 使用堆缓冲区而不是直接缓冲区
那么就不需要调用System.gc()。
在HotSpot中还存在一个ExplicitGCInvokesConcurrent选项,也许IBM的VM有类似的功能。
非常感谢!我们还可以增加-XX:MaxDirectMemorySize的设置来延迟达到此限制并调用System.gc()吗?查看OpenJDK的v7版本,如果在MaxDirectMemorySize阈值下没有足够的直接内存可用,会抛出OOM错误。考虑到这一点,禁用System.gc()可能是个坏主意,而应该使用上述GC调优技术来延迟或避免调用。
是的,您可以这样做,但我没有涵盖这个选项,因为您已经自己提到了它。