Статьи

7 аргументов JVM высокоэффективных приложений

На момент написания этой статьи (март 2020 года) существует более 600 аргументов, которые вы можете передать JVM только вокруг сбора мусора и памяти. Если вы включите другие аспекты, общее количество аргументов JVM легко превысит 1000+. ?. Это слишком много аргументов для того, чтобы кто-нибудь переварил и понял. В этой статье мы выделим семь важных аргументов JVM, которые могут оказаться полезными.

1. -Xmx и -XX: MaxMetaspaceSize

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

1
-Xmx2g

Размер кучи играет решающую роль в определении вашего

а. Производительность приложения

б. Билл, что вы получите от своего облачного провайдера (AWS, Azure,…)

Это вызывает вопрос, каков правильный размер кучи для моего приложения? Должен ли я выделить большой размер кучи или маленький размер кучи для моего приложения? Ответ: «Это зависит». В этой статье мы поделились своими мыслями о том, нужно ли вам использовать большой или маленький размер кучи.

1
You might also consider reading this article: advantages of setting -Xms and -Xmx to same value.

Metaspace — это область, в которой будут храниться определения метаданных JVM, такие как определения классов, определения методов. По умолчанию объем памяти, который можно использовать для хранения этой информации метаданных, не ограничен (т. Е. Ограничен объемом ОЗУ вашего контейнера или машины). Вам необходимо использовать аргумент -XX: MaxMetaspaceSize, чтобы указать верхний предел объема памяти, который можно использовать для хранения информации метаданных.

1
-XX:MaxMetaspaceSize=256m

2. Алгоритм ГХ

На дату (март 2020 года) в OpenJDK есть 7 различных алгоритмов GC:

а. Serial GC

б. Параллельный GC

с. Concurrent Mark & ​​Sweep GC

д. G1 GC

е. Шенандоа GC

е. Z GC

грамм. Эпсилон GC

Если вы не укажете алгоритм GC явно, то JVM выберет алгоритм по умолчанию. До Java 8 Parallel GC является алгоритмом GC по умолчанию. Начиная с Java 9, G1 GC является алгоритмом GC по умолчанию.

Выбор алгоритма GC играет решающую роль в определении производительности приложения. Основываясь на наших исследованиях, мы наблюдаем отличные результаты производительности с алгоритмом Z GC. Если вы работаете с JVM 11+, то вы можете рассмотреть возможность использования алгоритма Z GC (то есть -XX: + UseZGC). Более подробную информацию об алгоритме Z GC можно найти здесь .

Ниже в таблице приведены аргументы JVM, которые необходимо передать для активации каждого типа алгоритма сборки мусора.

GC Алгоритм Аргумент JVM
Serial GC -XX: + UseSerialGC
Параллельный GC -XX: + UseParallelGC
Concurrent Market & Sweep (CMS) GC -XX: + UseConcMarkSweepGC
G1 GC -XX: + UseG1GC
Шенандоа GC -XX: + UseShenandoahGC
Z GC -XX: + UseZGC
Эпсилон GC -XX: + UseEpsilonGC

3. Включить ведение журнала GC

Журналы сборки мусора содержат информацию о событиях сбора мусора, освобождении памяти, длительности паузы, … Вы можете включить журнал сбора мусора, передав следующие аргументы JVM:

От JDK 1 до JDK 8:

1
-XX:+PrintGCDetails -XX:+PrintGCDateStamps -Xloggc:{file-path}

От 9 JDK и выше:

1
-Xlog:gc*:file={file-path}

Пример:

1
2
3
-XX:+PrintGCDetails -XX:+PrintGCDateStamps -Xloggc:/opt/workspace/myAppgc.log
 
-Xlog:gc*:file=/opt/workspace/myAppgc.log

Обычно журналы GC используются для настройки производительности сборки мусора. Тем не менее, журналы GC содержат важные микрометрические показатели. Эти показатели могут быть использованы для прогнозирования доступности приложения и характеристик производительности. В этой статье мы хотели бы выделить одну такую ​​микрометрию: « пропускная способность GC » (чтобы узнать больше о других доступных микрометриках, вы можете обратиться к этой статье ). Пропускная способность GC — это количество времени, которое ваше приложение тратит на обработку транзакций клиентов по сравнению с количеством времени, которое оно тратит на обработку операций GC. Скажем, если пропускная способность GC вашего приложения составляет 98%, то это означает, что приложение тратит 98% своего времени на обработку действий клиента, а оставшиеся 2% тратятся на работу GC.

Теперь давайте посмотрим на график использования кучи здоровой JVM:

Рис. График использования кучи работоспособной JVM (сгенерирован https://gceasy.io )

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

Теперь давайте посмотрим на график использования кучи больной JVM:

Рис. График использования кучи больной JVM (сгенерирован https://gceasy.io )

Вы можете заметить в правой части графика, хотя GC многократно запускается, использование памяти не падает. Это классический признак того, что приложение страдает от какой-то проблемы с памятью.

Если вы внимательно посмотрите на график, вы заметите, что повторные полные сборы мусора начали происходить примерно в 8 часов утра. Тем не менее, приложение начинает получать OutOfMemoryError только около 8:45 утра. До 8 часов утра пропускная способность приложения составляла около 99%. Но сразу после 8 утра пропускная способность ГХ начала падать до 60%. Потому что при повторном запуске GC приложение не будет обрабатывать какие-либо транзакции клиентов и будет выполнять только действия GC. В качестве упреждающей меры, если вы заметите, что пропускная способность GC начинает падать, вы можете удалить JVM из пула балансировщика нагрузки. Так что нездоровая JVM не будет обрабатывать новый трафик. Это минимизирует влияние клиента.

Рис. Повторное полное GC происходит задолго до OutOfMemoryError

Вы можете отслеживать микрометрические данные, связанные с ГХ, в режиме реального времени, используя GCeasy REST API .

4. -XX: + HeapDumpOnOutOfMemoryError, -XX: HeapDumpPath

OutOfMemoryError — это серьезная проблема, которая повлияет на SLA доступности / производительности вашего приложения. Чтобы диагностировать OutOfMemoryError или любые проблемы, связанные с памятью, нужно было бы захватить дамп кучи прямо сейчас или за несколько секунд до того, как приложение начнет испытывать OutOfMemoryError. Поскольку мы не знаем, когда OutOfMemoryError будет сгенерирован, трудно перехватить дамп кучи вручную в правильное время, когда он будет сгенерирован. Однако захват дампов кучи можно автоматизировать, передав следующие аргументы JVM:

-XX: + HeapDumpOnOutOfMemoryError и -XX: HeapDumpPath = {HEAP-DUMP-FILE-PATH}

В ‘-XX: HeapDumpPath’ необходимо указать путь к файлу, в котором должен храниться дамп кучи. Когда вы передаете эти два аргумента JVM, дампы кучи будут автоматически записываться и записываться в определенный путь к файлу, когда выбрасывается OutOfMemoryError. Пример:

1
-XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/crashes/my-heap-dump.hprof

После захвата дампов кучи вы можете использовать такие инструменты, как HeapHero , EclipseMAT для анализа дампов кучи.

Более подробную информацию об аргументах OutOfMemoryError JVM можно найти в этой статье .

5. -Xss

Каждое приложение будет иметь десятки, сотни, тысячи потоков. Каждый поток будет иметь свой собственный стек. В стеке каждого потока хранится следующая информация:

а. Методы / функции, которые в настоящее время выполняются

б. Примитивные типы данных

с. переменные

д. указатели объектов

е. возвращаемые значения

Каждый из них потребляет память. Если их потребление выходит за определенные пределы, генерируется StackOverflowError. Более подробную информацию о StackOverflowError и ее решении можно найти в этой статье . Однако вы можете увеличить ограничение размера стека потока, передав аргумент -Xss. Пример:

1
-Xss256k

Если вы установите для этого значения -Xss огромное число, память будет заблокирована и потрачена впустую. Предположим, что вы присваиваете значение -Xss равным 2 МБ, тогда как для этого требуется всего 256 КБ, тогда вы в конечном итоге будете тратить огромное количество памяти, а не только 1792 КБ (т.е. 2 МБ — 256 КБ). Вам интересно, почему?

Предположим, что ваше приложение имеет 500 потоков, тогда со значением -Xss, равным 2 МБ, ваши потоки будут использовать 1000 МБ памяти (т.е. 500 потоков x 2 МБ / поток). С другой стороны, если вы выделили -Xss только для 256 КБ, тогда ваши потоки будут использовать только 125 МБ памяти (т.е. 500 потоков x 256 КБ / поток). Вы сэкономите 875 МБ (т.е. 1000 — 125 МБ) памяти на JVM. Да, это будет иметь огромное значение.

Примечание. Потоки создаются вне кучи (т. Е. -Xmx), поэтому эти 1000 МБ будут добавлены к значению -Xmx, которое вы уже присвоили. Чтобы понять, почему темы создаются вне кучи, вы можете посмотреть этот короткий видеоклип .

Мы рекомендуем начинать с низкого значения (скажем, 256 КБ). Выполните тщательную регрессию, производительность и тестирование AB с этим параметром. Только если вы испытываете StackOverflowError, тогда увеличивайте значение, в противном случае попробуйте придерживаться более низкого значения.

6. -Dsun.net.client.defaultConnectTimeout и -Dsun.net.client.defaultReadTimeout

Современные приложения используют многочисленные протоколы (например, SOAP, REST, HTTP, HTTPS, JDBC, RMI …) для связи с удаленными приложениями. Иногда удаленным приложениям может потребоваться много времени для ответа. Иногда это может не отвечать вообще.

Если у вас нет правильных настроек тайм-аута, и если удаленные приложения не отвечают достаточно быстро, потоки / ресурсы вашего приложения будут зависать. Отказ удаленных приложений может повлиять на доступность вашего приложения. Это может привести к тому, что ваше приложение будет остановлено. Чтобы обеспечить высокую доступность вашего приложения, необходимо настроить соответствующие параметры времени ожидания.

Вы можете передать эти два мощных сетевых свойства времени ожидания на уровне JVM, которые могут быть глобально применимы ко всем обработчикам протоколов, которые используют java.net.URLConnection:

  1. sun.net.client.defaultConnectTimeout указывает время ожидания (в миллисекундах) для установления соединения с хостом. Например, для HTTP-соединений это время ожидания при установлении соединения с HTTP-сервером.
  2. sun.net.client.defaultReadTimeout указывает время ожидания (в миллисекундах) при чтении из входного потока при установлении соединения с ресурсом.

Пример, если вы хотите установить эти свойства на 2 секунды:

1
2
-Dsun.net.client.defaultConnectTimeout=2000
-Dsun.net.client.defaultReadTimeout=2000

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

7. -Duser.timeZone

Ваше приложение может иметь чувствительные бизнес-требования в отношении времени / даты. Например, если вы создаете торговое приложение, вы не можете принять транзакцию до 9:30. Для реализации этих бизнес-требований, связанных со временем и датой, вы можете использовать объекты java.util.Date, java.util.Calendar. Эти объекты по умолчанию собирают информацию о часовом поясе из базовой операционной системы. Это станет проблемой; если ваше приложение работает в распределенной среде. Посмотрите на следующие сценарии:

а. Если ваше приложение работает в нескольких центрах обработки данных, скажем, в Сан-Франциско, Чикаго, Сингапуре, то JVM в каждом центре обработки данных будут иметь разный часовой пояс. Таким образом, JVM в каждом центре обработки данных будут демонстрировать различное поведение. Это приведет к противоречивым результатам.

б. Если вы развертываете свое приложение в облачной среде, приложения могут быть перемещены в разные центры обработки данных без вашего ведома. В этом случае ваше приложение также может привести к другим результатам.

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

Чтобы избежать этих ссор, настоятельно рекомендуется установить часовой пояс на JVM с помощью системного свойства -Duser.timezone. Например, если вы хотите установить часовой пояс EDT для своего приложения, вы сделаете:

1
-Duser.timezone=US/Eastern

Вывод

В этой статье мы попытались обобщить некоторые важные аргументы JVM и их положительное влияние. Мы надеемся, что вы найдете это полезным.

Опубликовано на Java Code Geeks с разрешения Рама Лакшманана, партнера нашей программы JCG . Смотрите оригинальную статью здесь: 7 аргументов JVM для высокоэффективных приложений

Мнения, высказанные участниками Java Code Geeks, являются их собственными.