【问题标题】:Setting -XX:+DisableExplicitGC in production: what could go wrong?在生产环境中设置 -XX:+DisableExplicitGC:会出现什么问题?
【发布时间】:2023-03-14 23:34:01
【问题描述】:

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

测量结果表明,Java 部分中的每个计算都花费了几秒钟。所以我的第一个建议是在 VM 中启用垃圾收集日志记录。我们可以立即看到制作了许多 stop-the-world 的完整 GC。谈到这一点,java部分的开发者告诉我们,他们曾多次System.gc()“确保内存在使用后被释放”。

好的,我将不再详细说明该声明... ;-)

然后我们也添加了上述-XX:+DisableExplicitGC 虚拟机参数并重新运行测试。每次计算增加了大约 5 秒。

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

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

【问题讨论】:

  • 由于System.gc 无论如何都不能保证,从技术上讲,您不会破坏任何东西,但可能发生的情况是您在某些已经破坏的代码中触发了错误。不过,这并没有什么安慰。
  • 当然它某事,这就是它存在的原因:) 但是,如果它什么都不做,它仍然会尊重它的合同。因此,任何在-XX:+DisableExplicitGC 下发生故障的代码都会被破坏。
  • 更多证据表明,几乎永远不要尝试对 GC 进行二次猜测并自己调用 System.gc() :)
  • @Axel:就其价值而言,您可能不太可能在不知道这一点的情况下使用 NIO 直接缓冲区。它们似乎是用于处理大型、长寿命字节缓冲区的 IO 优化,这听起来不像是在业务线 webapp 中出现的东西。
  • @millimoose 大多数现代缓存实现都使用直接缓冲区进行堆外存储。但我猜缓存实现不会干扰堆的常规操作。

标签: java tomcat garbage-collection


【解决方案1】:

您并不是唯一一个通过设置-XX:+DisableExplicitGC 标志来修复stop-the-world GC 事件的人。不幸的是(尽管文档中有免责声明),许多开发人员认为他们比 JVM 更清楚何时收集内存并准确引入此类问题。

我知道-XX:+DisableExplicitGC 改善了生产环境的许多实例和零个出现任何负面影响的实例。

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

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

【讨论】:

  • 在此说明:“许多开发人员认为他们比 JVM 更了解”。实际上这是 Java 文档的一个问题。 System.gc 的描述没有说明它是一个危险的命令。我看过太多建议使用System.gc 常规 来释放内存的教程。 docs.oracle.com/javase/7/docs/api/java/lang/System.html#gc()
【解决方案2】:

我一直在努力解决同样的问题,根据我能够找到的所有信息,这肯定存在一些风险。根据 @millimoose 和 https://bugs.openjdk.java.net/browse/JDK-6200079 的原始帖子中的 cmets,如果使用 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 库的确切影响的一些澄清:Impact of setting -XX:+DisableExplicitGC when NIO direct buffers are used

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

还需要注意的是,除了NIO库之外,RMI分布式垃圾回收的JDK内部实现也调用了System.gc(): Unexplained System.gc() calls due to Remote Method Invocation System.gc() calls by core APIs

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

“但是,在大多数情况下,定期的 GC 活动就足够了 有效的 DGC"

“在大多数情况下”限定符对我来说听起来非常不切实际,所以再一次,似乎至少有一些风险只是关闭所有 System.gc() 调用,你最好修复尽可能在您的代码中调用,并且仅在万不得已的情况下完全关闭它们。

【讨论】:

【解决方案3】:

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-01-26
    • 1970-01-01
    • 2011-04-14
    相关资源
    最近更新 更多