Java挂了几秒钟,但在gc日志中未找到gc暂停

时间:2018-07-16 07:22:58

标签: java linux garbage-collection g1gc

我有一个使用g1 gc在Linux(CentOS 7)上运行的Java应用程序,它经常挂起几秒钟,看起来就像gc暂停一样,但是我在gc日志中找不到这么长时间的暂停。

为确保Java应用程序挂起,我启动了一个后台线程,该线程除了每500毫秒打印一次日志外,不执行其他任何操作。发现日志暂停了几秒钟。这是日志,它已在 [14:31:02,834] 暂停到 [14:31:05,677]

WARN [2018-07-16 14:30:57,831][clock]py.datanode.DataNodeAppEngine(196):tick...
WARN [2018-07-16 14:30:58,331][clock]py.datanode.DataNodeAppEngine(196):tick...
WARN [2018-07-16 14:30:58,832][clock]py.datanode.DataNodeAppEngine(196):tick...
WARN [2018-07-16 14:30:59,332][clock]py.datanode.DataNodeAppEngine(196):tick...
WARN [2018-07-16 14:30:59,832][clock]py.datanode.DataNodeAppEngine(196):tick...
WARN [2018-07-16 14:31:00,333][clock]py.datanode.DataNodeAppEngine(196):tick...
WARN [2018-07-16 14:31:00,833][clock]py.datanode.DataNodeAppEngine(196):tick...
WARN [2018-07-16 14:31:01,333][clock]py.datanode.DataNodeAppEngine(196):tick...
WARN [2018-07-16 14:31:01,834][clock]py.datanode.DataNodeAppEngine(196):tick...
WARN [2018-07-16 14:31:02,334][clock]py.datanode.DataNodeAppEngine(196):tick...
WARN [2018-07-16 14:31:02,834][clock]py.datanode.DataNodeAppEngine(196):tick...
WARN [2018-07-16 14:31:05,677][clock]py.datanode.DataNodeAppEngine(196):tick...
WARN [2018-07-16 14:31:06,177][clock]py.datanode.DataNodeAppEngine(196):tick...
WARN [2018-07-16 14:31:06,678][clock]py.datanode.DataNodeAppEngine(196):tick...
WARN [2018-07-16 14:31:07,178][clock]py.datanode.DataNodeAppEngine(196):tick...

同时显示gc日志(grep与应用程序线程停止的总时间):

2018-07-16T14:30:58.327+0800: 2679.423: Total time for which application threads were stopped: 0.3750533 seconds, Stopping threads took: 0.0003430 seconds
2018-07-16T14:31:05.176+0800: 2686.272: Total time for which application threads were stopped: 0.5037637 seconds, Stopping threads took: 0.0004556 seconds
2018-07-16T14:31:06.978+0800: 2688.074: Total time for which application threads were stopped: 0.0060367 seconds, Stopping threads took: 0.0003190 seconds

此外,此Java进程还具有一些运行本机代码的线程,它们是用C语言编写的,不受jvm的影响。这些线程运行良好,我非常确定这是因为这些线程之一是心跳线程,并且心跳超时为800MS,但是在暂停期间未找到心跳超时。

我还监视了CPU的使用情况,具有12个内核的CPU的闲置率高达80%。

内存使用也不是太高,THP(透明大页面)和交换内存也被禁用。

我发现一件事我无法理解:

在暂停附近总是有一个并发标记开始,并且无论何时发生并发标记,也都会有一个暂停。

2018-07-16T14:30:58.489+0800: 2679.586: [GC concurrent-mark-start]

我知道并发标记阶段不会导致STW,但是我不敢相信这只是一个巧合,因为我已经多次重现了这一点,并且总是这样。

这是YourKit暂停之一时的CPU使用率和内存使用率:

enter image description here enter image description here

由于@jspcal的建议,我得到了SafepointStatistics:

         vmop                    [threads: total initially_running wait_to_block]    [time: spin block sync cleanup vmop] page_trap_count
2566.430: G1IncCollectionPause             [     745          0              0    ]      [     0     0  2705     3   474    ]  0

G1IncCollectionPause花了将近3秒到达安全点,而旋转和阻止时间均为0

2 个答案:

答案 0 :(得分:2)

尽管GC是VM暂停的一种来源,但是安全点(停止世界暂停)可以通过其他操作(例如刷新代码缓存,有偏锁,某些调试操作等)来启动。这是list of safepoint operations。要对这些安全点进行故障排除,请使用以下选项:

安全点:

-XX:+UnlockDiagnosticVMOptions
-XX:+PrintSafepointStatistics
-XX:PrintSafepointStatisticsCount=1
-XX:+SafepointTimeout
-XX:SafepointTimeoutDelay=500
-XX:+LogVMOutput
-XX:LogFile=/var/log/jvm/vm.log

GC:

-verbose:gc
-XX:+PrintGCApplicationStoppedTime
-XX:+PrintGCApplicationConcurrentTime
-XX:+PrintGCDateStamps
-XX:+PrintGCDetails 
-Xloggc:/var/log/jvm/garbage.log
-XX:+UseGCLogFileRotation
-XX:NumberOfGCLogFiles=10
-XX:GCLogFileSize=100M

答案 1 :(得分:1)

最后,我发现这是由jdk的关于大型参考数组的并发标记的错误引起的:

Large reference arrays cause extremely long synchronization times

我的解决方案是将大型参考数组更改为分段的二维数组