Java зависает на несколько секунд, но в журнале gc пауза не обнаружена

У меня есть java-приложение, работающее в Linux (CentOS 7) с использованием g1 gc, и оно регулярно зависает на несколько секунд, это выглядит так же, как паузы 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. Эти потоки работали хорошо, я совершенно уверен в этом, потому что один из этих потоков является потоком сердцебиения, а время ожидания сердцебиения составляет 800 мс, но во время паузы тайм-аут сердцебиения не был обнаружен.

Я также следил за использованием процессора, и процессор с 12 ядрами простаивал до 80%.

Использование памяти также не слишком велико, THP (прозрачные огромные страницы) и память подкачки также были отключены.

И я нашел одну вещь, которую я не могу понять:

Рядом с паузой всегда находится concurrent-mark-start, и везде, где происходит concurrent-mark-start, также есть пауза.

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

Я знаю, что одновременная фаза меток не вызовет STW, но я не могу поверить, что это просто совпадение, потому что я воспроизводил это много раз, и это всегда так.

А вот загрузка ЦП и использование памяти во время одной из пауз от YourKit:

введите здесь описание изображения введите здесь описание изображения

Благодаря предложению @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.


person T.Tony    schedule 16.07.2018    source источник
comment
Если ваше приложение привязано к ЦП, вы можете голодать в потоках ведения журнала. Вы должны начать строить график использования кучи, если вы действительно думаете, что приложение находится в глубоком сборщике мусора. Возможно, взгляните на некоторые анализаторы памяти/JVM visualvm.github.io/download.html   -  person flakes    schedule 16.07.2018
comment
@flakes Я обновил использование процессора и памяти   -  person T.Tony    schedule 16.07.2018


Ответы (2)


В то время как GC является одним из источников пауз виртуальной машины, точки сохранения (остановка мировых пауз) могут быть инициированы другими операциями, такими как очистка кеша кода, предвзятая блокировка, определенные операции отладки и т. д. Вот список операций с точками сохранения. Чтобы устранить неполадки с этими точками сохранения, используйте следующие параметры:

Сейфпойнты:

-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
person jspcal    schedule 16.07.2018
comment
Я получил журнал, G1IncCollectionPause занял много времени. В то время как вращение и блокировка заняли 0, как я могу узнать, что вызывает такую ​​паузу? - person T.Tony; 16.07.2018
comment
С помощью SafepointTimeout вы увидите, какие потоки не смогли вовремя достичь безопасной точки. - person jspcal; 16.07.2018
comment
Я добавил -XX:+SafepointTimeout -XX:SafepointTimeoutDelay=2000, но тайм-аут не распечатывался. И еще, спин и блок равны 0, синхронизация очень большая (7 секунд). Мало того, что G1IncCollectionPause занял так много времени, любая операция может привести к этому. Означает ли это, что весь процесс был заморожен? - person T.Tony; 16.07.2018

Наконец, я обнаружил, что это вызвано ошибкой jdk в отношении параллельной маркировки для больших ссылочных массивов:

Большие ссылочные массивы вызывают чрезвычайно долгую синхронизацию

И мое решение состоит в том, чтобы изменить большой ссылочный массив на разделенные двумерные массивы.

person T.Tony    schedule 19.07.2018