Как настроить jvm аргументы

Опции JVM. Как это работает

С каждым днем слово java все больше и больше воспринимается уже не как язык, а как платформа благодаря небезызвестному invokeDynamic. Именно поэтому сегодня я бы хотел поговорить про виртуальную java машину, а именно — об так называемых Performance опциях в Oracle HotSpot JVM версии 1.6 и выше (server). Потому что сегодня почти не встретить людей, которые знают что-то больше чем -Xmx, -Xms и -Xss. В свое время, когда я начал углубляться в тему, то обнаружил огромное количество интересной информации, которой и хочу поделится. Отправной точкой, понятное дело, послужила официальная документация от Oracle. А дальше — гугл, эксперименты и общение:

-XX:+DoEscapeAnalysis

Начну, пожалуй, с самой интересной опции — DoEscapeAnalysis. Как многие из Вас знают, примитивы и ссылки на объекты создаются не в куче, а выделяются на стеке потока (256КБ по умолчанию для Hotspot). Вполне очевидно, что язык java не позволяет создавать объекты на стеке на прямую. Но это вполне себе может проделывать Ваша JVM 1.6 начиная с 14 апдейта.

Про то, как работает сам алгоритм можно прочитать тут (PDF). Если коротко, то:

  • Если область видимости объекта не выходит за область метода, в котором он создается, то такой объект может быть создан на фрейме стека вместо кучи (на самом деле не сам объект, а его поля, на совокупность которых заменяется объект);
  • Если объект не покидает область видимости потока, то к такому объекту другие потоки не имеют доступа и следовательно все операции синхронизации над объектом могут быть удалены.

Для реализации данного алгоритма строится и используется так называемый — граф связей (connection graph), по которому на этапе анализа (алгоритмов анализа — несколько) осуществляется проход для нахождения пересечений с другими потоками и методами.
Таким образом после прохода графа связей для любого объекта возможно одно из следующих следующих состояний:

  • GlobalEscape — объект доступен из других потоков и из других методов, например статическое поле.
  • ArgEscape — объект был передан как аргумент или на него есть ссылка из объекта аргумента, но сам он не выходит из области видимости потока в котором был создан.
  • NoEscape — объект не покидает область видимости метода и его создание может быть вынесено на стек.

После этапа анализа, уже сама JVM проводит возможную оптимизацию: в случае если объект NoEscape, то он может быть создан на стеке; если объект NoEscape или ArgEscape, то операции синхронизации над ним могут быть удалены.

Следует уточнить, что на стеке создается не сам объект а его поля. Так как JVM заменяет цельный объект на совокупность его полей (спасибо Walrus за уточнение).

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

скорость выполнения может увеличится в 8-15 раз. Хотя, на казалось бы, очевидных случаях из практики о которых недавно писалось (тут и тут) EscapeAnalys не работает. Подозреваю, что это связано с размером стека.

Кстати, EscapeAnalysis как раз частично ответственен за известный спор про StringBuilder и StringBuffer. То есть, если Вы вдруг в методе использовали StringBuffer вместо StringBuilder, то EscapeAnalysis (в случае срабатывания) устранит блокировки для StringBuffer’а, после чего StringBuffer вполне превращается в StringBuilder.

-XX:+AggressiveOpts

Опция AggressiveOpts является супер опцией. Не в том плане, что она резко увеличивает производительность Вашего приложения, а в том смысле, что она всего лишь изменяет значения других опций (на самом деле, это не совсем так — в исходном коде JDK довольно не мало мест, где AggressiveOpts изменяет поведение JVM, помимо упомянутых опций, один из примеров тут). Проверять измененные флаги будем с помощью двух команд:

После выполнения разница в результатах выполнения команд выглядела так:

-AggressiveOpts +AggressiveOpts
AutoBoxCacheMax 128 20000
BiasedLockingStartupDelay 4000 500
EliminateAutoBox false true
OptimizeFill false true
OptimizeStringConcat false true

Иными словами, все что делает эта опция — изменяет 5 данных параметров виртуальной машины. Причём, для версий 1.6 update 35 и 1.7 update 7 никаких отличий замечено не было. Данная опция по умолчанию отключена и в клиентском моде ничего не изменяет.
Расcмотрим, что же java подразумевает под агрессивной оптимизацией:

-XX:AutoBoxCacheMax=size

Позволяет расширить диапазон кешируемых значений для целых типов при старте виртуальной машины. Эту опцию я уже упоминал тут (второй абзац).

-XX:BiasedLockingStartupDelay=delay

Как известно, synchronized блок в java может быть представлен одним из 3-х видов блокировок:

  • biased
  • thin
  • fat

Подробней про это можно прочитать тут, тут и тут.

Так как большинство объектов (синхронизированных) блокируются максимум 1 одним потоком, то такие объекты могут быть привязаны (biased) к этому потоку и операции синхронизации над этим объектом внутри потока сильно удешевляются. Если к biased объекту пытается получить доступ другой поток, то происходит переключение блокировки для этого объекта на thin блокировку.

Читайте также:  Не работает морозилка двухкамерного холодильника

Само переключение относительно дорого, поэтому на старте JVM существует задержка, которая по умолчанию создает все блокировки как thin и если никакой конкуренции не обнаружено и код используется одним и тем же потоком, то такие блокировки, после истечения задержки, становятся biased. То есть, JVM пытается на старте определить сценарии использования блокировок и соответственно использует меньше переключений между ними. Соответственно, выставляя BiasedLockingStartupDelay в ноль, мы рассчитаем на то, что основные куски кода синхронизации будут использоваться лишь одни и тем же потоком.

-XX:+OptimizeStringConcat

Тоже довольно интересная опция. Распознает паттерн на подобии

и вместо постоянного выделения памяти под новую операцию конкатенации, идет попытка вычислить общее количество символов каждого объекта конкатенации для выделения памяти только 1 раз.
Иными словами, если мы вызовем 20 раз операцию append() для строки длинной 20 символов. То создание массива char произойдет один раз и длиной 400 символов.

XX:+OptimizeFill

Циклы заполнения/копирования массивов заменяются на прямые машинные инструкции для ускорения работы.
Например, следующий блок (взято из Arrays.fill()):

будет полностью замен на соответствующие процессорные инструкции на подобии сишных memset, memcpy только более низкоуровневых.

XX:+EliminateAutoBox

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

-XX:+UseCompressedStrings

Довольно спорная опция по моему убеждению… Если в далеких 90-х разработчики java не пожалели 2 байта на символ, то сегодня такая оптимизация смотрится довольно нелепо. Если кто не догадался, то опция заменяет в строках символьные массивы на байтовые, где это возможно (ASCII). По сути:

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

-XX:+UseStringCache

Довольно загадочная опция, судя по названию она должна каким-то образом кешировать строки. Как? Не понятно. Информации нету. Да и по коду похоже она ничего не выполняет. Буду рад, если кто-то сможет прояснить.

-XX:+UseCompressedOops

Для начала несколько фактов:

  • Размер указателя на объект в 32-х разрядной JVM составляет 32 бита. В 64-х разрядной — 64 бита. Следовательно, в первом случае Вы можете использовать адресное пространство размером 2^32 байт (4 ГБ), а во втором случае 2^64 байт.
  • Размер объектов в java кратен 8 байтам не зависимо от разрядности виртуальной машины (это не для всех виртуальных машин правда, но речь о Hotspot). То есть, при использовании 32-х разрядных указателей последние 3 бита будут всегда нулями, фактически, виртуальная машина реально использует лишь 29 бит.

Данная опция позволяет уменьшить размер указателя для 64-х разрядных JVM до 32-х бит, но в этом случае размер кучи ограничен 4 ГБ, поэтому, в дополнение к сокращенному указателю, используется свойство о кратности 8 байтам. В результате получаем возможность использовать адресное пространство размером 2^35 байт (32 ГБ) имея указатели в 32 бита.
Фактически, внутри виртуальной машины, мы имеем указатели на объекты, а не конкретные байты в памяти. Понятное дело, что из-за подобных допущений (о кратности) появляются дополнительные расходы на преобразование указателей. Но по сути это всего лишь одна операция сдвига и суммирования.

Помимо уменьшения размеров самих указателей, эта опция уменьшает также заголовки объектов и разного рода выравнивания и сдвиги внутри созданных объектов, что позволяет в среднем уменьшить потребление памяти на 20-60% в зависимости от модели приложения.

То есть, из недостатков имеем лишь:

  • Максимальный размер кучи ограничен 32 ГБ (64ГБ для JRockit при кратности объектов 16 байтам);
  • Появляются доп. расходы на преобразование JVM ссылок в нативные и обратно.

Так как для большинства приложений опция несет одни плюсы, то начиная с JDK 6 update 23 она включена по умолчанию, так же как и в JDK 7. Детальней тут и тут.

-XX:+EliminateLocks

Опция, которая устраняет лишние блокировки путем их объединения. Например следующие блоки:

будут преобразованы соответственно в

Таким образом сокращается количество попыток захвата монитора.

Заключение

700 флагов в одну статью довольно трудно. Я специально не затрагивал опции по тюнингу сборщика, так это довольно обширная и сложная тема и она заслуживает нескольких постов. Надеюсь статья была вам полезной.

Источник

Как выделить Java больше оперативной памяти

Из-за взаимодействия программного компонента Java с разработанными продуктами могут возникать ошибки, решение которых лежит на плечах пользователя. Оно достигается двумя путями: переустановкой модуля и выделением дополнительной памяти Java. С каждой ситуацией стоит разобраться отдельно.

Зачем увеличивать память Java

Задачу по увеличению Java памяти пользователи ставят перед собой в следующих случаях:

  • Не запускается игра Minecraft. Геймер получает сообщение, что для запуска не хватает виртуальной памяти, хотя минимальные требования по оперативке соблюдены.
  • Проблема с памятью кучи Java. Написанное серверное приложение не запускается. Для его полноценной работы требуется 512 Мб оперативки на компьютере, но трудности с запуском возникают даже при имеющихся 4 Гб.

Исправить проблему можно двумя способами.

Как выделить память Java

Выделить Джава-модулю больше оперативной памяти возможно через «Панель управления». Способ удобнее рассмотреть на примере проблем с запуском игры Minecraft.

Инструкция:

Читайте также:  Работаем не покладая рук вид

  1. Открывается «Панель управления».
  2. В поиске нужно найти Java-модуль.
  3. После запуска ПО в шапке выбирается раздел Java.
  4. В запустившемся окне открывается View.
  5. Для корректной работы модуля удалите лишние строки, если они есть. Должна остаться только одна, где указана последняя версия ПО. Важно обратить внимание на разрядность.
  6. Для увеличения памяти производится изменение столбца Runtime Parameters. При этом параметры записываются в следующем виде: -Xincgc-Xmx2048M, где 2048 – 2 Гб выделяемой оперативки. Важно писать без пробелов. В 32-битной ОС рекомендуется выделение 768 Мб.
  7. Нажимается ОК, ОС перезагружается.

Расшифровка используемых команд:

  • Xincgc – освобождает неиспользуемые объекты из памяти;
  • Xmx – максимальный объем оперативки;
  • Xms – минимальный объем.

Если это не помогло запустить Minecraft, переустановите модуль Java и игру. После удаления очистите реестр с помощью CCleaner.

Увеличение памяти с помощью переменных среды

Увеличить оперативную память в Джаве можно с помощью переменных системной среды. В виртуальной машине прописываются два аргумента, упомянутых ранее: -Xms и -Xmx.

Чтобы система воспринимала написанные аргументы, нужно добавить переменную с названием «_JAVA_OPTIONS».

Если количество памяти, отведенной для работы Java, в два раза меньше имеющейся оперативки, то команды прописываются по следующей инструкции:

  1. Открываются «Свойства» на ярлыке «Мой компьютер».
  2. Из левой части выбираются «Дополнительные параметры системы».
  3. На вкладке «Дополнительно» производится одиночный клик по «Переменные среды».
  4. Нажимается кнопка «Создать».
  5. Имя переменной: «_JAVA_OPTIONS», аргументы: «-Xms512m -Xmx1024m».

В примере объем оперативки составлял 1 Гб.

Видео: 3 способа выделить больше памяти Java.

Таким образом в статье рассмотрено два метода увеличения оперативной памяти, выделяемой для работы Java-модуля.

Источник

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

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

1. -Xmx и -XX: MaxMetaspaceSize

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

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

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

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

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

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

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

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

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

с. Concurrent Mark & ​​Sweep 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:

От 9 JDK и выше:

Пример:

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

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

Читайте также:  Asus windows 10 почему не работает тачпад

Рис. График использования кучи работоспособной 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 =

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

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

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

5. -Xss

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

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

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

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

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

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

Если вы установите для этого значения -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 секунды:

Источник

Оцените статью