Статьи

Настройка производительности Java: максимально эффективное использование сборщика мусора

Что происходит под капотом JVM и как сборщик мусора влияет на производительность Java?

Мир настройки производительности — это опасное место, один флаг JVM вышел из равновесия, и все может быстро стать опасным. По этой причине мы решили обратиться к Хаиму Ядиду , эксперту по настройке производительности Java и создателю монадического профилировщика JVM mjprof. В этом посте мы поделимся некоторыми из его проверенных в бою прозрений и узнаем, как внутренности JVM ведут себя в условиях стресса.

Новая статья: Настройка производительности Java — Как получить максимальную отдачу от вашего сборщика мусора http://t.co/NnzQpuWBHz pic.twitter.com/8zqMrUfSHl

— Такипи (@takipid) 2 апреля 2015 г.

Понимая, с чем ты сталкиваешься

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

Основная ловушка: игнорирование выбросов

То, как такого рода поведение можно не заметить и никого не насторожить, заключается в одной распространенной ошибке: измерении среднего времени транзакции и игнорировании выбросов. Вот где проблемы GC скрываются: хотя большую часть времени система может вести себя нормально, в других случаях ее отзывчивость идет на юг и вызывает у многих пользователей плохой опыт. Например, транзакция, которая обычно занимает 100 мс, подвергается GC-паузе и внезапно занимает несколько секунд или даже минуту. На сайте электронной коммерции это может остаться незамеченным для всех, кроме пользователя, если сопровождающие системы смотрят только на среднее время транзакции. Еще одна проблема, которую легко упустить из виду, — это когда пропускная способность системы снижается, скажем, на 20%, и она не реализует свой потенциал. Возможно, вы никогда не узнаете, что что-то пошло не так, потому что вы не смотрите на правильные показатели. Во многих случаях причина заключается в низкой осведомленности о затратах GC и сосредоточении внимания на одной метрике среднего времени отклика, игнорируя 99-й процентиль.

Определение требований к производительности: частота и продолжительность

Главный вопрос здесь заключается в следующем: что вы видите в качестве приемлемых критериев для частоты и продолжительности паузы ГХ в вашем приложении? Например, ежедневная пауза в 15 секунд может быть приемлемой, в то время как частота один раз в 30 минут будет абсолютной катастрофой для продукта. Требования исходят из области каждой системы, в которой системы торговли в режиме реального времени и с высокой частотой предъявляют самые строгие требования.

В целом, паузы продолжительностью 15-17 секунд не редкость. Некоторые системы могут даже достигать 40-50 секунд пауз, и у Хаима также была возможность увидеть 5-минутные паузы в системе с большой кучей, которая выполняла задания пакетной обработки. Так что длительность паузы не играет большой роли там.

Остановите мир и соберите данные: важность журналов GC

Самым богатым источником данных о состоянии сборки мусора в системе на основе JSM HotSpot являются журналы GC. Если ваша JVM не генерирует журналы GC с временными метками, вы упускаете критически важный источник данных для анализа и решения возникающих проблем. Это верно для сред разработки, постановки, нагрузочного тестирования и, самое главное, в производстве. Вы можете получить данные обо всех событиях GC в вашей системе, независимо от того, были ли они выполнены одновременно или вызвали паузу «остановка мира»: сколько времени они заняли, сколько ЦП они израсходовали и сколько памяти было освобождено. Из этих данных вы сможете понять частоту и продолжительность этих пауз, их накладные расходы и перейти к выполнению действий по их уменьшению.

1
-XX:+PrintGCDetails -XX:+PrintGCDateStamps -Xloggc:mygclogfilename.gc

Минимальные настройки для сбора данных журнала GC

Если посмотреть на показатели, обычно 5% — это верхняя граница для приемлемых издержек GC, в то время как допустимые паузы сильно отличаются от одного приложения к другому.

Два инструмента, которые стоит упомянуть здесь для анализа журналов GC, это GC Viewer с открытым исходным кодом, который доступен на Github , и jClarity Censum .

Стратегии решения

Как только мы получим необходимую информацию, пришло время изучить возможные причины и решения. Каждое внесенное вами изменение требует нового теста и серии сбора журналов, чтобы оценить его эффективность и определить, помогло ли оно нам сдвинуть иглу и выполнить требования. Желательно в производстве и в условиях стресса. Существует 4 основных способа решения проблемы, вызванной паузами в GC: переключение сборщика мусора, настройка флагов, управляющих кучей Java , внесение изменений в код и использование альтернативных JVM / сборщиков. Вот краткий обзор подходов для рассмотрения в области HotSpot и типа проблем, которые они решают:

1. Неправильный сборщик мусора в игре

Грубо говоря, JVM имеет 4 сборщика мусора, и вы можете выбрать, какой из них использовать при запуске. Чтобы узнать больше о каждом типе вы можете проверить сравнение прямо здесь . Одна из распространенных причин проблем с сборкой мусора — использование неправильного сборщика для типа приложения, которое вы разрабатываете. По умолчанию в HotSpot используется коллектор Parallel / Throughput, и зачастую это не лучший выбор для вашего приложения. Выбор правильного коллектора (с помощью флагов JVM) — это утверждение ваших приоритетов в системе и, как правило, первая проблема, которую следует рассмотреть. Обычно коллекторы CMS и G1, которые в основном работают одновременно, будут вызывать менее частые паузы. Хотя, когда наступает пауза, ее продолжительность, вероятно, будет больше, чем вызванная сборщиком Parallel, поскольку их резервный механизм однопоточный (Ой). С другой стороны, параллельный коллектор достигнет более высокой пропускной способности при том же размере кучи. Другое руководство относится к предсказуемости, если предсказуемая производительность является важным фактором, а размер кучи невелик, ответ может быть найден параллельным сборщиком. И если среднее время отклика / задержка является вашим главным приоритетом, то CMS или G1, скорее всего, ответ.

2. Настройка кучи Java

После выбора предпочтительного алгоритма GC пора заняться настройкой. Измерьте (через GC logs) пропускную способность и распределение времени паузы, и если вы довольны этим, то все готово. Если накладные расходы GC высоки (а пропускная способность низкая), обычно увеличение размера кучи улучшит ситуацию. Когда дело доходит до решения длинных пауз CMS или G1, ситуация более деликатная. Другая причина этого, помимо фрагментации, заключается в том, что JVM не может справиться со скоростью, с которой объекты перемещаются в старый ген из нового поколения, а затем ему необходимо приостановить приложение, чтобы исправить это. Решением здесь является либо запуск GC раньше, либо увеличение размера кучи.

По опыту размеры кучи обычно колеблются от 1 до 8 ГБ, большие размеры встречаются гораздо реже. Увеличение размера кучи более 8 ГБ в процессе настройки обычно происходит, когда вы становитесь отчаянным. Жизнеспособная причина больших размеров кучи — это когда мы хотим создать большой кеш, но это также можно решить из кучи.

Давайте рассмотрим другой пример, чтобы показать, где необходима настройка скорости разлива. Скажем, приложению нужно 100 МБ для обработки некоторого запроса, а новый размер генерации составляет 50 МБ. Объекты, которых не должно быть в старом поколении, достигнут там в кратчайшие сроки. Для локализации этой проблемы потребуется настройка пространства нового поколения и выживших, а также убедитесь, что недолговечные объекты закончат свою жизнь в новом поколении. Основными факторами, которые здесь играют, являются размер кучи, соотношение между новым и старым генами, размер пространства выживших и максимальный порог владения — сколько циклов GC требуется для перемещения объекта к старому гену.

Другим важным фактором, который мы должны принять во внимание, является «живая настройка» приложения. Имея в виду размер объектов, которые сохраняются в памяти в течение длительных периодов, примером для liveset будет аппликативный кеш, который содержит частые наборы результатов запросов к БД. При настройке JVM необходимо убедиться, что «liveset» удобно размещен в старом поколении, и в этом регионе имеется достаточно свободной памяти в дополнение к этому потреблению. Невыполнение этого требования приведет к серьезному повреждению поведения JVM, что приведет к низкой пропускной способности и частым паузам.

3. Архитектура и изменения кода

Некоторые проблемы заставят нас прибегнуть к коду и, возможно, даже к архитектурным изменениям. Одной из причин неприятностей, которые мы можем здесь решить, является фрагментация. Длинные паузы с коллектором CMS могут быть вызваны фрагментацией старого поколения. Каждый цикл GC освобождает куски памяти от старого поколения и делает его похожим на швейцарский сыр, пока не наступит момент, когда JVM просто не сможет с этим справиться . Это происходит, когда JVM перемещает объекты нового поколения, которые больше, чем эти «дыры», и затем он должен остановить приложение, чтобы решить проблему. Приложения с большим состоянием, которое изменяется со временем, неизбежно вызывают фрагментацию. Поскольку состояние меняется со временем, объекты «старого состояния» будут освобождены от старого поколения, в то время как их состояние замены будет создано в новом поколении. Когда он в конечном итоге будет продвигаться к старому поколению, он, вероятно, не будет соответствовать нужному месту, и это приведет к фрагментации.

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

4. Альтернативные JVM и сборщики мусора

Если время паузы имеет решающее значение для вашего приложения и JVM Hotspot не может обеспечить приемлемое время ответа, есть еще два возможных варианта. Первый — это Azul Zing JVM с безостановочным сборщиком мусора C4. Чтобы начать использовать Zing, вам понадобится относительно большой компьютер и размер кучи, начиная с 32 ГБ. Еще один вариант, который еще недостаточно развит, но, возможно, стоит попробовать, если вам нравится жить на грани, — это алгоритм GC Шенандоа . Он использует технику, известную как указатель переадресации ручья, что приводит к сверхнизким паузам с разумными издержками

Дальнейшее чтение: ведущие эксперты GC

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

Вывод

Сборка мусора является одной из самых интересных тем в JVM, и мы надеемся, что этот пост помог вам лучше понять различные движущиеся части. Большое спасибо Хаиму Ядиду, который согласился поделиться с нами своим опытом! Если у вас есть какие-либо вопросы или вы хотели бы получить разъяснения, сообщите нам об этом в разделе комментариев ниже.