Как разумно спланировать настройку производительности jvm

Java JVM

Это третья статья из серии по оптимизации JVM:

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

1. Разберитесь со сборщиком мусора jvm

2. Иметь представление об общих инструментах для мониторинга производительности jvm.

3. Возможность читать журнал gc

4. Не настраивайтесь на настройку, настройка jvm не может решить все проблемы с производительностью


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


Эта статья основана на настройке производительности jvm в сочетании с различными параметрами jvm для настройки приложения. Основное содержание:

1. Общий процесс настройки jvm

2. Несколько показателей производительности, связанных с настройкой jvm

3. Некоторые принципы настройки jvm, которые необходимо освоить

4. Стратегии настройки и примеры


Во-первых, уровень настройки производительности

Чтобы повысить производительность системы, нам необходимо оптимизировать все аспекты и уровни системы. Ниже приведены несколько уровней, которые необходимо оптимизировать.

Из вышеизложенного мы видим, что помимо настройки jvm, есть еще несколько уровней, которыми нужно заниматься, поэтому настройка для системы — это не только настройка jvm, а общая настройка системы для улучшения производительность системы. Эта статья предназначена только для объяснения настройки jvm, а другие аспекты будут представлены позже.

Перед выполнением настройки jvm мы предполагаем, что настройка архитектуры проекта и настройка кода были выполнены или являются оптимальными для текущего проекта. Эти два элемента являются основой настройки JVM, а настройка архитектуры оказывает наибольшее влияние на систему. Мы не можем ожидать приложения с ошибочной системной архитектурой или бесконечной оптимизацией на уровне кода. Благодаря настройке JVM можно добиться качественного скачка. Это невозможно. .

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


Во-вторых, процесс настройки jvm

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

1. Определение производительности

Чтобы найти и оценить узкие места в производительности, мы должны сначала узнать определение производительности.Для настройки jvm нам нужно знать следующие три определяющих свойства, которые являются основой для оценки:

  • Пропускная способность: один из важных показателей, он относится к наивысшим показателям производительности, которые сборщик мусора может поддерживать приложением, независимо от времени паузы или потребления памяти, вызванного сборкой мусора.
  • Задержка: его показатель заключается в сокращении времени паузы из-за сборки мусора или полном устранении паузы из-за сборки мусора, чтобы избежать дрожания во время работы приложения.
  • Объем памяти: объем памяти, необходимый сборщику мусора для бесперебойной работы.

Среди этих трех атрибутов улучшение производительности любого из них происходит почти за счет потери производительности одного или двух других атрибутов.Важно, чтобы определить на основе бизнес-потребностей приложения.

2. Принципы настройки производительности

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

1. Принцип утилизации MinorGC: каждый второстепенный GC должен собирать как можно больше мусорных объектов. Уменьшить частоту полного GC в приложении.

2. Принцип максимизации памяти GC: при решении проблем с пропускной способностью и задержкой, чем больше памяти может использовать обработчик мусора, тем лучше эффект сборки мусора и тем плавнее будет работать приложение.

3. Принцип выбора 2 из 3 для настройки сборщика мусора: в атрибутах производительности, пропускной способности, задержке и использовании памяти мы можем выбрать для настройки только два из них, а не оба.

3. Процесс настройки производительности


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

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

Что касается режима работы JVM, мы напрямую выбираем режим сервера, который также является официально рекомендуемым режимом после jdk1.6.

Что касается сборщика мусора, мы напрямую используем параллельный сборщик по умолчанию в jdk1.6-1.8 (новое поколение принимает parallelGC, а старое поколение использует parallelOldGC).


3. Определите использование памяти

Прежде чем определить объем памяти, нам нужно знать две точки знаний:

  1. Фаза выполнения приложения
  2. выделение памяти JVM


1. Этап операции

Стадию выполнения приложения я могу разделить на следующие три фазы:

1. Фаза инициализации: JVM загружает приложение и инициализирует основные модули и данные приложения.

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

3. Этап сводки. На заключительном этапе сводки выполняются некоторые эталонные тесты для создания соответствующего отчета о политике. Мы можем игнорировать это на данном этапе.

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


2. Распределение памяти и параметры JVM

Основное пространство в куче jvm состоит из вышеупомянутого нового поколения, старого поколения и постоянного поколения.Весь размер кучи = размер нового поколения + размер старого поколения + размер постоянного поколения. Здесь мы не будем слишком подробно описывать конкретный метод продвижения объекта, давайте посмотрим на некоторые параметры команды jvm и укажем размер кучи. Если вы не укажете следующие параметры, виртуальная машина автоматически выберет подходящее значение, а также автоматически отрегулирует его в зависимости от нагрузки на систему.


Поколение

параметр

описывать

размер кучи

-Xms

Начальный размер кучи, по умолчанию 1/64 физической памяти (


-Xmx

Максимальный размер кучи, значение по умолчанию (параметр MaxHeapFreeRatio можно настроить), когда свободная память кучи больше 70%, JVM уменьшит кучу до минимального предела -Xms

Кайнозой

-XX:NewSize

Начальное значение размера пространства нового поколения


-XX:MaxNewSize

Максимальный размер пространства нового поколения


-Xmn

Размер пространства нового поколения, здесь размер (эдем+2 выживших)

постоянное поколение

-XX:PermSize

Начальное значение и минимальное значение пространства постоянной генерации


-XX:МаксПермСизе

Максимальное значение пространства постоянной генерации

старость

Размер пространства старого поколения неявно устанавливается в соответствии с размером молодого поколения.



Начальное значение = -Xmx минус значение -XX:NewSize



Min = значение -Xmx минус значение -XX:MaxNewSize


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


3. Рассчитайте активный размер данных

Расчет активного размера данных должен следовать следующему процессу:


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

Расчет оперативных данных должен обеспечивать выполнение следующих условий:

1. Во время теста параметры запуска используют параметры jvm по умолчанию, а не задаются вручную.

2. Убедитесь, что приложение находится в стабильной фазе, когда происходит полная сборка мусора.

Начиная с параметров jvm по умолчанию, нужно наблюдать за использованием памяти, требуемым приложением на стабильной фазе.


Какая стадия считается стабильной?

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


Когда будет установлено, что приложение находится в стабильной стадии, обратите внимание на журнал GC приложения, особенно на полный журнал GC.

Команды журнала GC: -XX:+PrintGCTimeStamps -XX:+PrintGCDetails -Xloggc:

Журналы GC — лучший способ собрать информацию, необходимую для настройки. Даже в производственной среде вы можете включить журналы GC для обнаружения проблем. Включение журналов GC оказывает минимальное влияние на производительность, но может предоставить подробные данные.


Должен быть журнал FullGC, если нет, вы можете использовать инструмент мониторинга, чтобы вызвать вызов один раз, или использовать следующую команду, вы также можете вызвать

jmap -histo:live pid


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

Из приведенных выше журналов gc мы, вероятно, можем проанализировать заполнение кучи и время GC всего приложения, когда происходит fullGC.Конечно, чтобы быть более точным, его следует собрать несколько раз, чтобы получить среднее значение. Или используйте самый длинный FullGC для оценки.


На приведенном выше рисунке после fullGC пространство старого поколения занимает 93168 КБ (около 93 МБ), которые мы используем в качестве активных данных пространства старого поколения.

Выделение другого пространства кучи основано на следующих правилах..

космос

Параметры команды

Рекомендуется расширить несколько

java heap

-Xms и -Xmx

В 3-4 раза больше пространства старого поколения после FullGC

постоянное поколение

-XX:PermSize

-XX:MaxPermSize

В 1,2-1,5 раза больше, чем постоянное место в диапазоне после FullGc

Кайнозой

-Xmn

В 1-1,5 раза больше пространства старого поколения после FullGC

старость


В 2-3 раза больше пространства старого поколения после FullGC

Основываясь на приведенных выше правилах и информации FullGC на приведенном выше рисунке, теперь мы можем спланировать пространство кучи приложения следующим образом:

пространство кучи java: 373 МБ (= старое пространство 93168 КБ * 4)

Пространство нового поколения: 140 МБ (= пространство старого поколения 93168 КБ * 1,5)

Пространство для постоянной генерации: 5 МБ (= пространство для постоянной генерации 3135 КБ * 1,5)

Пространство старого поколения: 233Mb=пространство кучи-пространство просмотра нового поколения=373Mb-140Mb

Соответствующие параметры запуска приложения должны быть:

java -Xms373m -Xmx373m -Xmn140m -XX:PermSize=5m -XX:MaxPermSize=5m


В-четвертых, настройка задержки

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

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


1. Требования к системной задержке

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

  • Среднее время простоя, приемлемое для приложения: это время сравнивается с измеренной продолжительностью Minor GC.
  • Приемлемая частота незначительных GC: частота незначительных GC сравнивается с допустимым значением.
  • Максимально допустимое время паузы: максимальное время паузы сравнивается с продолжительностью FullGC в наихудшем случае.
  • Максимально допустимая частота пауз: в основном частота FullGC.

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

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

  • продолжительность MinorGC;
  • Подсчитайте время MinorGC;
  • Худшая продолжительность FullGC;
  • В худшем случае частота FullGC;


2. Оптимизировать размер нового поколения


Например, в приведенном выше журнале gc мы видим, что средняя продолжительность Minor GC = 0,069 секунды, а частота Minor GC составляет один раз каждые 0,389 секунды.

Если среднее мертвое время, установленное нашей системой, составляет 50 мс, то текущие 69 мс явно слишком велики и нуждаются в корректировке.

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

Если вы хотите уменьшить его продолжительность, вам нужно уменьшить размер его пространства.

Если вы хотите уменьшить его частоту, вам нужно увеличить размер его пространства.


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

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

java -Xms359m -Xmx359m -Xmn126m -XX:PermSize=5m -XX:MaxPermSize=5m

新生代的大小有140m变为126,堆大小顺应变化,此时老年代是没有变化的。


3. Оптимизация размера старости

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


На рисунке выше мы можем видеть

FullGC 平均频率 =5.8s

FullGC 平均持续时间=0.14s

(以上为了测试,真实项目的fullGC 没有这么快)


Есть ли способ оценить без журналов Full GC?

Мы можем рассчитать его по скорости подъема объекта.


Скорость подъема объекта

Например, в приведенных выше параметрах запуска наш старенький размер = 233Mb.

Так что сколько времени потребуется, чтобы заполнить 233 Мб свободного места в старом поколении, зависит от скорости продвижения от молодого поколения к старому поколению.

Каждый раз, когда занятость старого поколения увеличивается = занятость кучи java после каждого MinorGC минус занятость места новым поколением после MinorGC

Коэффициент продвижения объекта = средний (заполняемость старого поколения на продвижение), деленный на пространство старого поколения

С помощью скорости продвижения объекта мы можем рассчитать, сколько второстепенных GC необходимо для заполнения пространства старого поколения, что можно рассчитать примерно за один полный GC.


Например:

На картинке выше:

第一次minor GC 之后,老年代空间:13740kb - 13732kb =8kb

第二次minor GC 之后,老年代空间:22394kb - 17905kb =4489kb

第三次minor GC 之后,老年代空间:34739kb - 17917kb =16822kb

第四次minor GC 之后,老年代空间:48143kb - 17913kb =30230kb

第五次minor GC 之后,老年代空间:62112kb - 17917kb =44195kb

Скорость улучшения каждого второстепенного ГК в пожилом возрасте

4481kb 第二次和第一次minorGC之间

12333kb 第3次和第2次minorGC之间

13408kb 第4次和第3次minorGC之间

13965kb 第5次和第4次minorGC之间

Мы можем рассчитать:

每次minorGC 的平均提升为12211kb,约为12Mb

上图中,平均minorGC的频率为 213ms/次

提升率=12211kb/213ms=57kb/ms

老年代空间233Mb ,占满大概需要233*1024/57=4185ms 约为4.185s。


Ожидаемая продолжительность наихудшей частоты FullGC может быть оценена двумя вышеуказанными методами.Вы можете настроить размер старого поколения, чтобы настроить частоту FullGC.Конечно, если продолжительность FullGC слишком велика, чтобы удовлетворить требования наихудшей задержки для приложение, вам нужно Переключить удаление мусора. Конкретно как переходить, я расскажу в следующей статье.Например, при переходе на CMS метод настройки для CMS будет немного другим.


V. Настройка пропускной способности


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

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

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

Для сборщика мусора цель настройки производительности для повышения пропускной способности состоит в том, чтобы избегать или редко встречаться FullGC или сборщик мусора Stop-The-World Compression Garbage Collection (CMS), поскольку оба метода могут снизить пропускную способность приложения. Постарайтесь переработать как можно больше объектов на этапе MinorGC, чтобы избежать слишком быстрого продвижения объектов к старому возрасту.


6. Наконец

Согласно исследованию, проведенному корпорацией Plumbr по использованию конкретных сборщиков мусора, в данных исследования использовались 84 936 случаев. В 13% случаев, когда сборщик мусора был указан явно, чаще всего использовался параллельный сборщик (CMS), однако в большинстве случаев лучший сборщик мусора не выбирался. Эта доля составляет около 87%.


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

-----------------------------------------------------------------------------

Если вы хотите увидеть больше интересных и оригинальных технических статей, сканируйте и подписывайтесь на официальный аккаунт.

Сосредоточьтесь на личностном росте и развитии игр, а также способствуйте росту и прогрессу местного игрового сообщества.