Статистика сервера

Максимальный объем памяти, выделенный для JVM (значение Xmx 8000 м)
8 ГБМинимальный объем памяти, выделенный для JVM (значение Xms 8000 м)
8 ГБ Максимальный объем памяти, выделенный для JVM (значение Xmx 4000 м) — (1/4) от общего ) если не переопределено
4 ГБ Минимальная память по умолчанию, выделенная для JVM (значение Xmx 4000 м / 1/6 от общего ) если не переопределено 2 ГБ

Используемые инструменты

Jprofiler использовался для профилирования JVM в удаленном экземпляре.

Jmeter использовался для отправки нагрузки на ELB, к которому был прикреплен отслеживаемый экземпляр.

Предварительные условия для теста (настройки Jmeter)

мы хотели отправить ту же нагрузку на веб-сервис CP nextgen версии 4.1 и понаблюдать за его поведением под разными сборщиками мусора. приведенные ниже настройки использовались для создания нагрузки и подачи ее на ELB, к которому был подключен отслеживаемый экземпляр.

Ниже приведен скриншот из консоли Jmeter.

100 потоков (целевой параллелизм )

Время нарастания 1000 секунд

10 шагов повышения эффективности

500 секунд удержания целевой скорости

это означает

каждые 100 секунд будет добавляться 10 пользователей, пока мы не достигнем 100 пользователей (1000 секунд, разделенные на 10 шагов, равны 100 секундам на шаг. 100 пользователей, разделенные на 10 шагов, равны 10 пользователям на шаг. Итого 10 пользователей каждые 100 секунд).

После достижения 100 потоков все они будут продолжать работать и вместе обращаться к серверу в течение 500 секунд.

так что вся эта загрузка заняла 1000 секунд времени разгона + 500 секунд = 1500 секунд (25 минут)

вышеуказанные параметры использовались на протяжении PoC для поддержания обычной среды.

Поведение JVM и использование памяти веб-службой при значениях JVM по умолчанию (xms = 2 ГБ, xmx = 4 ГБ)

наше приложение под нагрузкой 20 потоков

наше приложение под нагрузкой 30 потоков

при нагрузке менее 30 потоков JVM зависла, поскольку мы направляли нагрузку на один экземпляр, а загрузка ЦП достигла 100%. поэтому ясно, что один экземпляр не может справиться с нагрузкой более чем из 25 или около того потоков.

Поведение JVM и использование памяти веб-службой при переопределенных значениях JVM (максимальный и минимальный размеры кучи равны 8 ГБ)

веб-сервис в режиме ожидания

Поведение JVM и использование памяти веб-службы при переопределенных значениях JVM и различных сборщиках мусора с включенным автоматическим масштабированием (с трафиком, отправленным в ELB вместо отправки непосредственно в экземпляр)

Веб-сервис под нагрузкой 100 потоков и сборщик мусора G1GC

то же изображение, что и выше, увеличенное

Между действиями gc существует примерно 12-секундный разрыв. Это лучше, чем большинство статистических данных, наблюдаемых ранее.

График GC ниже

наше приложение с -XX:+UseParNewGC (сборщик мусора CMS)

увеличенное изображение

активность cms gc

наше приложение с Serial Garbage Collector

наше приложение с параллельным сборщиком мусора

Вывод

выше приведено краткое изложение этого PoC

поведение облачного ценообразования веб-службы nextgen версии 4.1 с 4 доступными сборщиками мусора для java 8 и ниже.

эти изображения отражают только второстепенные действия и коллекции GC.

следовательно, мы можем подвести итог

Сборщик мусора CMS выполняет наиболее частые действия по сборке мусора, и между действиями почти нет промежутка, последовательный сборщик мусора работает немного лучше в этом аспекте, и между каждым второстепенным действием по сборке мусора есть интервал 5–6 секунд, параллельный сборщик мусора

с другой стороны, интервал между действиями GC составляет 7–8 секунд, первый сборщик мусора Garbage (GCG1) имеет интервал 12–13 секунд между действиями GC, что делает его предпочтительным GC для использования с cp nextgen ws 4.1 версии.

Таким образом, в целом второстепенный интервал активности сбора GC

GCG1 › Параллельный сборщик мусора › Последовательный сборщик мусора › CMS GC

когда мы принимаем во внимание использование памяти, CMS GC имеет очень мало используемой памяти из-за сбора мусора с высокой частотой, что увеличивает доступное пространство. последовательный сборщик мусора и параллельный сборщик мусора продемонстрировали почти одинаковую картину использования памяти, поскольку их частоты сборки мусора очень близки друг к другу. но GCG1 показывает довольно высокое использование памяти в данный момент времени по сравнению с другими экземплярами, поскольку его действия по сборке мусора не так часты, как в других случаях, что приводит к скачку процента используемой памяти в данный момент времени.

производительность нашего приложения с G1GC и размерами кучи, используемыми для nextgen 4.0 (xmx = 8 ГБ xms = 4 ГБ)

здесь cp nextgen 4.1 был протестирован с размерами кучи, используемыми для версии 4.0. если вы обратитесь к документации по профилированию для версии 4.0, максимальный размер кучи установлен на 8 ГБ, а минимальный размер кучи — на 4 ГБ.

Таким образом, поведение 4.1 было исследовано при двух разных значениях xms 4 ГБ и 8 ГБ, выше приведено сравнение результатов.

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

эффекты дедупликации строк при добавлении (подлежит уточнению)

Дедупликацию строк можно включить, добавив следующие флаги в команду java -jar.

-XX:+UseG1GC -XX:+UseStringDeduplication -XX:+PrintStringDeduplicationStatistics

Что такое дедупликация строк?

Представьте, что у вас есть телефонная книга, в которой есть люди, у которых есть String firstName и String lastName. А бывает, что в вашей телефонной книге у 100 000 человек одинаковые firstName = "Jon".

Поскольку вы получаете данные из базы данных или файла, эти строки не интернируются, поэтому ваша память JVM содержит массив символов {'J', 'o', 'n'} 100 тысяч раз, по одному на строку Джона. Каждый из этих массивов занимает, скажем, 20 байт памяти, поэтому эти 100 тыс. Jon занимают 2 МБ памяти.

Благодаря дедупликации JVM поймет, что «Jon» дублируется много раз, и заставит все эти строки Jon указывать на один и тот же базовый массив символов, уменьшив использование памяти с 2 МБ до 20 байт. (это еще предстоит изучить)

результаты дедупликации строк при локальном запуске

это приложение, которое запускается локально во время инициализации в течение примерно 10 минут. Как видите, дедупликация строк выполнялась 54 (выделено) раза и «дедуплицировала» строки 38,1 МБ. Дедупликация строк проверила 1941986 строк. Я полагаю, что при запуске в производственной среде под большой нагрузкой эти цифры будут увеличиваться, а дедупликация строк будет выполняться больше раз, что приведет к значительному количеству сброса кучи.

Используемые команды и флаги

  • XX:+UseParallelGC — параллельный сборщик мусора
  • -XX:+UseConcMarkSweepGC — сборщик мусора CMS
  • -XX:+UseG1GC — сборщик мусора G1
  • -XX:ParallelCMSThreads= (TBT)CMS Collector — количество используемых потоков

пример использования

java -XX:+UseParallelGC -Denv=$SERVER_ENVIRONMENT_VARIABLE -Dlogback.configurationFile=./logback.xml -jar -Xmx8000m -Xms8000m  cpwssm04.jar 8080 >/dev/null 2>&1 &

Другие необходимые команды

вы можете использовать команду ‘jps’, чтобы найти идентификатор процесса вашей банки, если она запущена

как только PID известен, вы можете использовать jcmd 10980 VM.flags ( jcmd {{pid here}} VM.flags ), который вернет приведенный ниже ответ. вы можете проверить, были ли заданные вами значения приняты JVM.

Ответ :

-XX:CICompilerCount=4 -XX:InitialHeapSize=6442450944 -XX:MaxHeapSize=7340032000 -XX:MaxNewSize=2446327808 -XX:MinHeapDeltaBytes=524288 -XX:NewSize=2147483967 -X4:29496 -XX=29496 :+UseCompressedClassPointers -XX:+UseCompressedOops -XX:+UseFastUnorderedTimeStamps -XX:+UseParallelGC

Возможная будущая работа

Если мы используем параллельный GC или G1GC, мы можем указать максимальное количество потоков и время паузы для сборки мусора, пропускную способность и занимаемую площадь (размер кучи).

  • Количеством потоков сборщика мусора можно управлять с помощью параметра командной строки -XX:ParallelGCThreads=‹N›.
  • Максимальное целевое время паузы (промежуток [в миллисекундах] между двумя GC) указывается с помощью параметра командной строки -XX:MaxGCPauseMillis=‹N›.
  • Целевая максимальная пропускная способность (измеряемая по времени, затраченному на сборку мусора, по сравнению со временем, затраченным вне сборки мусора) задается параметром командной строки -XX:GCTimeRatio=‹N›.
  • Максимальный размер кучи (объем памяти кучи, который требуется программе во время работы) указывается с помощью параметра -Xmx‹N›.

при необходимости мы можем изменить эти значения, чтобы получить желаемую оптимизацию производительности и поведения.

В Java 8u20 появился еще один параметр JVM для сокращения ненужного использования памяти за счет создания слишком большого количества экземпляров одной и той же String. Это оптимизирует память кучи. путем удаления повторяющихся значений String в глобальный одиночный массив char[].

Этот параметр можно включить, добавив -XX:+UseStringDeduplication в качестве параметра JVM. (Это можно напрямую использовать в нашей реализации)

Вопросы-Ответы

Почему минимальные максимальные размеры кучи устанавливаются для процесса, а не для самой JVM?

если он установлен на саму JVM, все другие Java-программы, работающие внутри экземпляра, также получат эти значения в JVM, что нежелательно (поскольку разные программы имеют разные требования к памяти, мы можем в конечном итоге использовать недостаточно или перераспределить память), но в нашем случае, поскольку мы запускаем только одну java-программу в одном экземпляре, устанавливая xmx и xms (минимальные максимальные значения кучи) для самой JVM, это не причинит никакого вреда, но установив его для процесса, у нас больше контроля, и мы можем изменить его всякий раз необходимо из кода развернуть скрипты без проблем.

Почему значение Xmx и значение Xms одинаковы (минимальный и максимальный размер кучи установлены на одно и то же значение)?

Если xms (минимальный размер кучи) установить на более низкое значение, приложение будет часто страдать от GC. Каждый раз запрашивая больше памяти от ОС с потреблением времени, и это ненужные накладные расходы.

Прежде всего, если ваше приложение критически важно для производительности, вам, безусловно, следует избегать свопинга страниц памяти на/с диска, поскольку это приведет к увеличению времени, затрачиваемого сборщиком мусора. Чтобы этого избежать, память можно заблокировать. Но если Xms и Xmx не совпадают, то память, выделенная после первоначального выделения, не будет заблокирована.

На производственной машине установка -Xms так же, как -Xmx, идеальна. Почему? Поскольку рабочая машина/экземпляр обычно является одноразовой машиной (то есть кроме ОС будет работать только сервер приложений — в нашем случае работает только веб-служба). И через некоторое время экземпляр захватит всю необходимую ему память кучи и не отпустит ее. Так что вы могли бы также дать ему всю память кучи для начала.

(Существует также небольшое снижение производительности из-за того, что JVM постоянно запрашивает у ОС дополнительное пространство в куче, а также из-за того, что JVM определяет, не нужно ли ей пространство или может ли оно отдавать место. Установка -Xms и -Xmx на одно и то же значение избегает выполнения этих вычислений. Но разница в производительности на современных процессорах и конфигурациях ОЗУ незначительна.) Единственным недостатком установки Xmx (минимальный размер кучи) на такое высокое значение является то, что больше время запуска приложения увеличится. но в нашем случае мы можем допустить небольшое дополнительное время запуска.

установка одинаковых значений xmx и xms нежелательна на машине разработки, поскольку мы также запускаем другие Java-программы.