Hotspot JVM — проблема с изменением размера кучи G1GC

Недавно я тестировал приложение для презентаций с относительно высокой одновременной нагрузкой. Приложение представляет собой Java-приложение и работает на Hotspot JVM (1.8.0_111).

Я мог получить максимальную пропускную способность около 400 TPS с кучей 4G и параллельным сборщиком пропускной способности. График пропускной способности (в зависимости от нагрузки) показан ниже.

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

Поскольку Oracle предлагает использовать G1GC для размеров кучи больше 4G, я хотел попробовать G1 и посмотреть, улучшит ли это каким-либо образом пропускную способность моего приложения.

К моему удивлению, с G1GC я увидел тенденцию пропускной способности ниже.

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

Я был искренне удивлен и решил покопаться в нем, чтобы посмотреть, что здесь произошло. Вот что я нашел.

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

Я вижу, что изначально из кучи 4G выделено 1,5G для регионов old gen и 2,5G для регионов eden. Но по мере того, как время идет, и старое поколение больше не может уместиться в 1,5G, размер кучи изменяется. Это кажется безобидным. Но проблема, похоже, в том, как происходит изменение размера.

Весь 4G теперь выделен регионам старого поколения и почти ни один регионам eden. Теперь, когда что-то нужно выделить для eden, размер кучи снова изменяется. И это становится новой нормой, когда размер кучи многократно изменяется, что приводит к огромным потерям производительности для приложения.

Кто-нибудь замечал это раньше с G1GC? Есть ли рекомендации по обсуждению этого вопроса?

Ниже приведена командная строка запуска с параметрами JVM.

java -server -Xms4096m -Xmx4096m -XX:MetaspaceSize=100m -XX:MaxMetaspaceSize=100m -XX:MaxDirectMemorySize=512m -XX:MinMetaspaceFreeRatio=0 -XX:MaxMetaspaceFreeRatio=100 -XX:CompressedClassSpaceSize=20m -XX:InitialCodeCacheSize=50m -XX:ReservedCodeCacheSize=50m -XX:+AlwaysPreTouch -XX:+DisableExplicitGC -XX:+PrintGCDetails -XX:+PrintGCDateStamps -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/var/tmp -Xloggc:/servers/logs/gc.log.2017-01-05-085234 -Djava.awt.headless=true -XX:+UnlockCommercialFeatures -XX:+FlightRecorder -Dio.netty.leakDetectionLevel=simple -XX:MaxDirectMemorySize=512m -Dadmin.connectors.http.port=9000 -Dproxy.connectors.http.port=8080 -Dcom.sun.management.jmxremote -Dcom.sun.management.jmxremote.port=8654 -Dcom.sun.management.jmxremote.local.only=false -Dcom.sun.management.jmxremote.authenticate=false -Dcom.sun.management.jmxremote.ssl=false -server -cp ...

Опции JVM:

-server 
-Xms4096m 
-Xmx4096m 
-XX:MetaspaceSize=100m 
-XX:MaxMetaspaceSize=100m 
-XX:MaxDirectMemorySize=512m 
-XX:MinMetaspaceFreeRatio=0 
-XX:MaxMetaspaceFreeRatio=100 
-XX:CompressedClassSpaceSize=20m 
-XX:InitialCodeCacheSize=50m 
-XX:ReservedCodeCacheSize=50m 
-XX:+AlwaysPreTouch 
-XX:+DisableExplicitGC 
-XX:+PrintGCDetails 
-XX:+PrintGCDateStamps 
-XX:+HeapDumpOnOutOfMemoryError 
-XX:HeapDumpPath=/var/tmp 
-Xloggc:/servers/logs/gc.log.2017-01-05-085234 
-Djava.awt.headless=true 
-XX:+UnlockCommercialFeatures 
-XX:+FlightRecorder 
-Dio.netty.leakDetectionLevel=simple 
-XX:MaxDirectMemorySize=512m 
-Dadmin.connectors.http.port=9000 
-Dproxy.connectors.http.port=8080 
-Dcom.sun.management.jmxremote 
-Dcom.sun.management.jmxremote.port=8654 
-Dcom.sun.management.jmxremote.local.only=false 
-Dcom.sun.management.jmxremote.authenticate=false 
-Dcom.sun.management.jmxremote.ssl=false 
-server 
-cp ...

Журналы gc можно найти здесь


person Sasidhar Sekar    schedule 05.01.2017    source источник
comment
Пожалуйста, не могли бы вы поделиться всеми параметрами, которые вы установили при запуске приложения? docs.oracle.com/javase/ 8/docs/technotes/guides/vm/gctuning/   -  person Saulius Next    schedule 05.01.2017
comment
@SauliusNext Теперь я добавил список параметров к вопросу. Для G1GC единственная разница заключается в добавлении -XX:+UseG1GC.   -  person Sasidhar Sekar    schedule 05.01.2017
comment
поскольку вы уже включили ведение журнала GC, вы должны прикрепить достаточно длинный журнал GC за интересующий период.   -  person the8472    schedule 05.01.2017
comment
@SasidharSekar я зарегистрировал проблему bugs.openjdk.java.net/browse/JDK-8172354 от вашего имени. Пожалуйста, ознакомьтесь с предложением, предоставленным старшим инженером по сборке мусора в Oracle Томасом Шацлем. Дайте мне знать, если вам нужны дополнительные разъяснения.   -  person Fairoz    schedule 09.01.2017
comment
Спасибо @Fairoz Это многое проясняет. Я попробую эти предложения, чтобы увидеть, как ситуация улучшится.   -  person Sasidhar Sekar    schedule 09.01.2017


Ответы (1)


По-видимому, существует множество из следующих двух причин GC:

  • [GC pause (G1 Humongous Allocation) (молодой) (начальный знак)
  • [GC пауза (G1 Evacuation Pause) (молодой) (в космос истощен)

Огромные ассигнования требуют места в старом поколении, исчерпание пространства приводит к увеличению размера молодого поколения. По сути, они конкурируют друг с другом.

Похоже, вы выделяете много огромных объектов (> 1/2 размера области G1) быстрее, чем параллельные циклы, инициированные IHOP, могут их собрать.

Можно попробовать увеличить размер области. Если это большие примитивные массивы (т. е. не ссылочный массив), то экспериментальная функция быстрого восстановления тоже может помочь.

person the8472    schedule 06.01.2017
comment
да. Мое приложение действительно выделяет много огромных объектов. К сожалению, функция возврата, на которую вы указываете, кажется, доступна только для OpenJDK (я использую Hotspot JVM). Но, прочитав комментарии, упомянутые вами и @sauliusNext, я думаю, что собираюсь поиграть со следующими вариантами, чтобы увидеть, как это поможет. -XX:MaxGCPauseMillis -XX:G1MixedGCLiveThresholdPercent=‹› -XX:G1MixedGCCountTarget=‹› -XX:G1OldCSetRegionThresholdPercent=‹› - person Sasidhar Sekar; 09.01.2017
comment
В конце всего упражнения я понял, что ваше предложение увеличить размер области решило мою проблему. Спасибо :-) - person Sasidhar Sekar; 10.01.2017