Java protobuf создает слишком много объектов

#java #garbage-collection #protocol-buffers

#java #сборка мусора #протокол-буферы

Вопрос:

У меня есть сервер jetty, который проксирует между серверной частью и конвертирует protobufs frontend.proto-> backend.proto и backend.proto-> frontend.proto.

Время выполнения запросов составляет менее 20 мс (99-й) и 40 мс (99,9-й), пока загрузка не достигла максимума.
Однако, когда нагрузка достигает максимума, 99-й увеличивается на 10, а 99,9-й увеличивается на 60.

Я исследовал это, и отложенные запросы вызваны паузами эвакуации GC, в этом я уверен, эти паузы занимают 50-70 мс и выполняются раз в 15 секунд при загрузке долины, но увеличиваются до одного раза в 3-5 секунд при пиковой нагрузке, продолжительность такая же.
Как только частота GC становится ниже 8-9 секунд, 99,9-й процентиль взлетает вверх, и я могу видеть журналы отладки медленных запросов одновременно с журналом GC.

Я профилировал с помощью JProfiler, Yourkit и VisualVM и увидел, что:

  • Пространство Eden заполняется, и запускается пауза GC
  • Очень немногие объекты перемещаются в Survivor (несколько МБ из 12G)
  • Таким образом, срок действия большинства объектов в Eden уже истек
  • Это имеет смысл, поскольку запросы занимают 30-40 мс, и большая часть времени жизни объектов привязана к времени жизни запроса
  • Большинство объектов создаются во время десериализации protobuf

Я пробовал играть с размерами GCPauseMillis и Eden, но, похоже, ничего не меняет, это никогда не занимает меньше 50 мс, а больший Eden означает меньшую частоту, но гораздо более длительные паузы

Я вижу здесь 2 варианта:

  1. Каким-то образом повторно использовать создание объектов в java-protobuf: кажется невозможным, прочитал множество сообщений и рассылок, и это не так настроено, они просто говорят, что «Распределение объектов Java очень эффективно, и оно должно быть способно обрабатывать многие создаваемые объекты», и хотя это правда, связанные с этим затраты на сборку убивают мои 99,9 тыс.
  2. Заставьте GC запускаться чаще, скажем, раз в секунду, чтобы сократить время сбора, чтобы оно останавливало больше запросов, но на более короткое время: я играл с размерами GCMaxMillis и Eden, но, похоже, я не могу уменьшить их

Я загрузил журнал gc в gc_log

Версия Java:

 java version "1.8.0_131"
Java(TM) SE Runtime Environment (build 1.8.0_131-b11)
Java HotSpot(TM) 64-Bit Server VM (build 25.131-b11, mixed mode)
  

Подробности GC:

 -XX:CICompilerCount=12 -XX:ConcGCThreads=5 -XX:ErrorFile=/home/y/var/crash/hs_err_pid%p.log -XX: FlightRecorder -XX:G1HeapRegionSize=4194304 -XX:GCLogFileSize=4194304 -XX: HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/home/y/logs/yjava_jetty -XX:InitialHeapSize=12884901888 -XX:MarkStackSize=4194304 -XX:MaxHeapSize=12884901888 -XX:MaxNewSize=7730102272 -XX:MinHeapDeltaBytes=4194304 -XX:NumberOfGCLogFiles=10 -XX: ParallelRefProcEnabled -XX: PrintGC -XX: PrintGCDateStamps -XX: PrintGCDetails -XX: PrintGCTimeStamps -XX: UnlockCommercialFeatures -XX: UseCompressedClassPointers -XX: UseCompressedOops -XX: UseFastUnorderedTimeStamps -XX: UseG1GC -XX: UseGCLogFileRotation
  

Комментарии:

1. Можете ли вы добавить к вопросу подробности о том, какую Jvm и сборщик мусора вы используете.

2. @SilverShroud готово!

3. Проверка выходных данных журнала, копирование объекта, похоже, вызывает всплеск. Но размер оставшегося в живых или старого поколения, похоже, не увеличивается в этих случаях (возможно, ошибка). Я бы попробовал обновленные версии java 8 runtime, чтобы посмотреть, ведет ли она себя так же. Вы также можете попробовать поиграть со следующими значениями, чтобы ограничить размер eden space -XX:G1NewSizePercent -XX:G1MaxNewSizePercent

4. @SilverShroud Я провел еще несколько тестов, и прикосновение к ручкам G1, похоже, не работает, хотя переключение на CMS помогло, задержка сильно снизилась, потому что эвакуация Eden, похоже, не приостанавливается с CMS. Использование CMS действительно кажется немного отсталым, в то время как G1 намного современнее.

5. Я думаю, что эти флаги являются экспериментальными флагами. Чтобы иметь возможность их использовать, должен быть установлен какой-то другой флаг. (посмотрите на это, если вы это пропустили)