Системная доступность многих служб Java с малой задержкой и высокой доступностью часто страдает из-за пауз GC.Как новое поколение сборщиков мусора с малой задержкой, ZGC имеет очень хорошую производительность в управлении памятью и утилизации больших объемов памяти с низкой задержкой. Сервисы. Эта статья начинается с боли GC, принципа ZGC, практики настройки ZGC и эффекта обновления ZGC. Я надеюсь, что эти практики помогут или вдохновят вас.
ZGC(Z Garbage Collector) — это сборщик мусора с малой задержкой, представленный в JDK 11. Цели его разработки включают:
- Время паузы не превышает 10 мс;
- Время паузы не увеличивается с размером кучи или размером активных объектов;
- Поддерживает кучи на уровне 8 МБ ~ 4 ТБ (в будущем будет поддерживаться 16 ТБ).
Из целей проектирования мы знаем, что ZGC подходит для управления памятью и повторного использования больших объемов памяти и служб с низкой задержкой. Эта статья в основном знакомит с приложением и отличной производительностью ZGC в сценариях с низкой задержкой.Содержание статьи в основном разделено на четыре части:
- Боль от ГК: представить болевые точки GC, с которыми приходится сталкиваться в реальном бизнесе, и проанализировать узкие места времени паузы сборщика CMS и сборщика G1;
- Принцип ЗГК: проанализируйте основную причину, по которой время паузы ZGC короче, чем у G1 или CMS, и технический принцип, лежащий в основе этого;
- Практика настройки ZGC: Сосредоточьтесь на обмене пониманием настройки ZGC и анализе нескольких реальных случаев настройки;
- Улучшить эффект ZGC: показывает эффект от применения ZGC в производственной среде.
Боль от ГК
Системная доступность многих служб Java с малой задержкой и высокой доступностью часто страдает от пауз GC. Пауза GC относится к STW (Stop The World) во время сборки мусора.Во время STW все потоки приложения останавливают свою деятельность и ждут окончания паузы GC. Возьмем в качестве примера службу управления рисками Meituan. Некоторые вышестоящие предприятия требуют, чтобы служба управления рисками возвращала результаты в течение 65 мс, а доступность должна достигать 99,99%. Но из-за пауз GC нам не удалось достичь вышеуказанных целей доступности. В то время использовался сборщик мусора CMS, и один Young GC занимал 40 мс, 10 раз в минуту, а среднее время отклика интерфейса составляло 30 мс. Путем расчета видно, что (40 мс + 30 мс) * 10 раз / 60000 мс = 1,12% времени ответа на запрос увеличится с 0 до 40 мс, из которых 30 мс * 10 раз / 60000 мс = 0,5% ответа на запрос время увеличится на 40 мс. Видно, что пауза GC больше влияет на время отклика. Чтобы уменьшить влияние пауз GC на доступность системы, мы внесли коррективы с точки зрения сокращения времени одиночной сборки мусора и уменьшения частоты сборки мусора, а также протестировали сборщик мусора G1, но эти три меры не смогли уменьшить влияние сборки мусора на сервисы. , Влияние доступности.
Узкое место времени паузы CMS и G1
Прежде чем внедрять ZGC, сначала просмотрите процесс GC в CMS и G1 и самое узкое место — время паузы. Young GC, G1 и ZGC нового поколения CMS основаны на алгоритме маркировки-копии, но конкретная реализация алгоритма приводит к огромным различиям в производительности.
Алгоритм пометки-копии используется в CMS молодого поколения (ParNew является сборщиком мусора молодого поколения по умолчанию для CMS) и сборщиком мусора G1. Алгоритм маркировки-копии можно разделить на три этапа:
- Фаза маркировки, начиная с коллекции GC Roots, маркировка активных объектов;
- Фаза передачи, то есть копирование активного объекта на новый адрес памяти;
- На этапе релокации адрес объекта изменился из-за переноса На этапе релокации все указатели на старый адрес объекта должны быть скорректированы на новый адрес объекта.
Взяв в качестве примера G1, с помощью процесса алгоритма метки-копии в G1 (этот алгоритм используют как Young GC, так и Mixed GC из G1) анализируется основное узкое место времени паузы G1. Цикл сборки мусора G1 показан на следующем рисунке:
Процесс гибридной переработки G1 можно разделить на фазу маркировки, фазу очистки и фазу репликации.
Отметить анализ фазы паузы
- Фаза начальной маркировки: Фаза начальной маркировки относится к процессу маркировки всех прямых дочерних узлов из корней GC.Этот этап называется STW. Из-за небольшого количества корней GC этот этап обычно занимает очень короткое время.
- Параллельная фаза маркировки. Параллельная фаза маркировки относится к анализу достижимости объектов в куче, начиная с корней GC, чтобы найти уцелевшие объекты. Эта фаза является параллельной, то есть поток приложения и поток GC могут быть активны одновременно. Параллельная маркировка занимает относительно много времени, но, поскольку это не STW, нас не слишком волнует продолжительность этой фазы.
- Фаза примечания: отметьте те объекты, которые изменились на этапе одновременной маркировки. Этот этап - STW.
Анализ остановки фазы очистки
- На этапе очистки подсчитываются разделы с уцелевшими объектами и разделы без уцелевших объектов На этом этапе не производится очистка мусорных объектов и не выполняется репликация уцелевших объектов. Этот этап - STW.
Анализ остановки фазы копирования
- Фаза передачи в алгоритме копирования требует выделения новой памяти и копирования переменных-членов объекта. Фаза передачи — STW, где выделение памяти обычно занимает очень короткое время, но копирование переменных-членов объекта может занять больше времени, поскольку время копирования пропорционально количеству уцелевших объектов и сложности объекта. Чем сложнее объект, тем больше времени требуется для копирования.
В четырех процессах STW начальная маркировка занимает меньше времени, потому что маркируются только корни GC. Перемаркировка занимает меньше времени из-за небольшого количества объектов. Этап очистки занимает меньше времени из-за небольшого количества разделов памяти. Фаза переноса занимает много времени, чтобы обработать все уцелевшие объекты. Следовательно, узким местом времени паузы G1 является в основном переходная стадия STW в метке-репликации. Почему фазу переноса нельзя выполнять одновременно с фазой маркировки? Основная причина в том, что G1 не решает проблему точного определения адреса объекта в процессе передачи.
Young GC G1 и Young GC CMS, STW всего процесса маркировки-репликации, здесь подробно описываться не будут.
Принцип ЗГК
Полностью параллельный ZGC
Подобно ParNew и G1 в CMS, ZGC также использует алгоритм маркировки-копирования, но ZGC внесла значительные усовершенствования в этот алгоритм: ZGC почти параллелен на этапах маркировки, передачи и перемещения, то есть время паузы реализации ZGC составляет менее 10 мс. критическая причина цели.
Цикл сборки мусора ZGC показан на следующем рисунке:
ZGC имеет только три фазы STW:начальная отметка,Примечание,первоначальный перевод. Среди них первоначальная маркировка и первоначальный перенос должны сканировать только все корни GC соответственно, а время обработки пропорционально количеству корней GC.Как правило, время очень короткое; время STW на этапе повторной маркировки составляет очень короткая, не более 1 мс, и если она превышает 1 мс, она снова войдет. Параллельная фаза маркировки. То есть почти все паузы в ZGC зависят только от размера набора GC Roots, и время паузы не увеличивается с размером кучи или размером активных объектов. В отличие от ZGC, метастатическая фаза G1 полностью STW, и время паузы увеличивается с размером выживших объектов.
Ключевая технология ZGC
ZGC решает проблему точного доступа к объектам в процессе передачи с помощью цветных указателей и технологии барьера чтения, а также реализует параллельную передачу. Общий принцип описывается следующим образом: «параллелизм» при параллельной передаче означает, что поток приложения постоянно обращается к объекту, в то время как поток GC передает объект. Допустим, объект передан, но адрес объекта вовремя не обновился, тогда поток приложения может получить доступ к старому адресу, вызвав ошибку. В ZGC поток приложения, обращающийся к объекту, активирует «барьер чтения». Если обнаружено, что объект перемещен, «барьер чтения» обновит указатель чтения на новый адрес объекта, так что поток приложения всегда обращается к Новый адрес объекта. Итак, как JVM определяет, что объект был перемещен? Это использование адреса ссылки на объект, то есть указателя окраски. Технические детали заштрихованных указателей и барьеров чтения описаны ниже.
Указатель затенения
Затенение указателей — это метод хранения информации в указателях.
ZGC поддерживает только 64-битные системы и делит 64-битное виртуальное адресное пространство на несколько подпространств, как показано на следующем рисунке:
Среди них [0~4 ТБ) соответствует куче Java, [4 ТБ ~ 8 ТБ) называется адресным пространством M0, [8 ТБ ~ 12 ТБ) называется адресным пространством M1, [12 ТБ ~ 16 ТБ) зарезервировано и не используется, [16 ТБ ~ 20 ТБ) называется переназначенным пространством.
Когда приложение создает объект, оно сначала запрашивает виртуальный адрес в пространстве кучи, но виртуальный адрес не сопоставляется с реальным физическим адресом. В то же время ZGC будет запрашивать виртуальный адрес для объекта в адресных пространствах M0, M1 и Remapped, и эти три виртуальных адреса соответствуют одному и тому же физическому адресу, но одновременно действует только одно из этих трех пространств. . Причина, по которой ZGC устанавливает три виртуальных адресных пространства, заключается в том, что он использует идею «пространства во времени» для сокращения времени паузы GC. Пространство в «Пространстве для времени» — виртуальное пространство, а не реальное физическое пространство. В последующих главах процесс переключения этих трех пространств будет подробно описан.
В соответствии с приведенным выше разделением адресного пространства ZGC фактически использует только 0-е место в 64-битном адресном пространстве.41-й и 42-йХранятся 45 бит метаданных, а биты с 47 по 63 фиксируются на 0.
ZGC хранит информацию о живучести объекта в 42~45 битах, что полностью отличается от традиционной сборки мусора, и помещает информацию о живучести объекта в заголовок объекта.
прочитать барьер
Барьер чтения — это метод, с помощью которого JVM вставляет небольшой фрагмент кода в код приложения. Этот код выполняется, когда поток приложения считывает ссылку на объект из кучи. Важно отметить, что этот код запускается только при «чтении ссылки на объект из кучи».
Прочитайте пример барьера:
Object o = obj.FieldA // 从堆中读取引用,需要加入屏障
<Load barrier>
Object p = o // 无需加入屏障,因为不是从堆中读取引用
o.dosomething() // 无需加入屏障,因为不是从堆中读取引用
int i = obj.FieldB //无需加入屏障,因为不是对象引用
Кодовая функция барьера чтения в ZGC: В процессе маркировки и передачи объекта используется для определения того, удовлетворяет ли адрес ссылки объекта условиям, и выполнения соответствующих действий.
Демонстрация параллельной обработки ZGC
Далее мы подробно представим процесс переключения вида адреса в цикле сборки мусора ZGC:
- инициализация: После инициализации ZGC адресное представление всего пространства памяти устанавливается на Remapped. Программа работает нормально, выделяет объекты в памяти и запускает сборку мусора после выполнения определенных условий, а затем переходит в фазу маркировки.
- Параллельная фаза маркировки: при первом входе в фазу маркировки представление имеет значение M0.Если к объекту обращается поток маркировки GC или поток приложения, адресное представление объекта изменяется с Remapped на M0. Итак, после завершения фазы маркировки адрес объекта либо вид M0, либо Remapped. Если адрес объекта — вид M0, объект активен, если адрес объекта — вид Remapped, объект неактивен.
- Параллельная фаза передачи: Войдите в фазу передачи после окончания метки, когда представление адреса снова устанавливается на Переназначение. Если к объекту обращается поток передачи GC или поток приложения, измените представление адреса объекта с M0 на Remapped.
На самом деле на этапе маркировки имеется два представления адресов M0 и M1, и описанный выше процесс показывает, что используется только одно представление адреса. Причина, по которой он предназначен для двух, заключается в том, чтобы различать предыдущую метку и текущую метку. То есть, после входа в фазу параллельной маркировки во второй раз, представление адреса корректируется на M1 вместо M0.
Цветные указатели и методы барьера чтения используются не только на этапе параллельной передачи, но и на этапе параллельной маркировки: чтобы установить объект как помеченный, традиционный сборщик мусора должен выполнить доступ к памяти и поместить информацию о выживании объекта в объект. заголовок; в то время как в ZGC вам нужно только установить 42 ~ 45-й бит адреса указателя, и, поскольку это доступ к регистру, он быстрее, чем доступ к памяти.
Практика настройки ZGC
ZGC не является «серебряной пулей» и нуждается в настройке в соответствии со специфическими особенностями сервиса. Реального боевого опыта, который можно найти в Интернете, немного, и теорию настройки нужно изучить самим.Мы также потратили много времени на этом этапе и, наконец, добились идеальной производительности. Одна из целей этой статьи — перечислить некоторые распространенные проблемы при использовании ZGC и помочь вам использовать ZGC для повышения доступности службы.
Основы настройки
Понимание важных параметров конфигурации ZGC
Возьмите конфигурацию параметра ZGC нашего сервиса в производственной среде в качестве примера, чтобы проиллюстрировать роль каждого параметра:
Пример настройки важного параметра:
-Xms10G -Xmx10G
-XX:ReservedCodeCacheSize=256m -XX:InitialCodeCacheSize=256m
-XX:+UnlockExperimentalVMOptions -XX:+UseZGC
-XX:ConcGCThreads=2 -XX:ParallelGCThreads=6
-XX:ZCollectionInterval=120 -XX:ZAllocationSpikeTolerance=5
-XX:+UnlockDiagnosticVMOptions -XX:-ZProactive
-Xlog:safepoint,classhisto*=trace,age*,gc*=info:file=/opt/logs/logs/gc-%t.log:time,tid,tags:filecount=5,filesize=50m
-Xms -Xmx: Максимальная память и минимальная память кучи здесь установлены на 10G, а память кучи программы останется неизменной на уровне 10G.-XX:ReservedCodeCacheSize -XX:InitialCodeCacheSize: Установить размер CodeCache, код, скомпилированный JIT, помещается в CodeCache, обычно для службы достаточно 64 или 128 м. Так как наш сервис имеет определенную специализацию, настройка относительно большая, о чем будет подробно рассказано позже.-XX:+UnlockExperimentalVMOptions -XX:+UseZGC: Конфигурация для включения ZGC.-XX:ConcGCThreads: потоки, которые одновременно собирают мусор. Значение по умолчанию — 12,5% от общего количества ядер, а для 8-ядерного ЦП — 1. После увеличения GC он будет потреблять ресурсы ЦП во время работы программы, что повлияет на пропускную способность.-XX:ParallelGCThreads: количество потоков, используемых на этапе STW, по умолчанию составляет 60% от общего количества ядер.-XX:ZCollectionInterval: минимальный интервал времени для выполнения ZGC в секундах.-XX:ZAllocationSpikeTolerance: Коэффициент коррекции адаптивного алгоритма запуска ZGC, по умолчанию равен 2, чем больше значение, тем раньше срабатывает ZGC.-XX:+UnlockDiagnosticVMOptions -XX:-ZProactive: Включить ли активный перезапуск, он включен по умолчанию, и конфигурация здесь означает, что он отключен.-Xlog: задайте содержимое, формат, расположение и размер каждого журнала в журнале GC.
Понимание синхронизации триггера ZGC
По сравнению с триггерным механизмом GC в CMS и G1, триггерный механизм GC в ZGC сильно отличается. Основной особенностью ZGC является параллелизм, и новые объекты всегда генерируются в процессе GC. Как гарантировать, что вновь сгенерированные объекты не заполнят кучу до завершения сборки мусора, является первой основной целью настройки параметров ZGC. Потому что в ZGC, когда куча заполняется до сборки мусора, это приводит к приостановке работающего потока, и продолжительность может достигать нескольких секунд.
ZGC имеет множество механизмов запуска GC, которые можно обобщить следующим образом:
- Инициируется блокировкой запросов на выделение памяти: когда сбор мусора слишком запоздал и он заполнит кучу, некоторые потоки будут заблокированы. Мы должны избегать этого триггера. Ключевое слово в журнале — «Задержка распределения».
- Адаптивный алгоритм, основанный на скорости выделения: самый важный метод запуска GC, принцип его алгоритма можно просто описать так: «ZGC вычисляет следующий GC на основе последней скорости выделения объектов и времени GC для запуска следующего GC, когда занятость памяти достигает порогового значения. ". Подробную теорию адаптивного алгоритма можно найти в книге Пэна Ченхана «Проектирование и реализация сборщика мусора нового поколения ZGC». Пороговое значение контролируется параметром ZAllocationSpikeTolerance, который по умолчанию равен 2. Чем больше значение, тем раньше запускается GC. Мы исправили некоторые проблемы, настроив этот параметр. Ключевое слово в журнале — «Коэффициент распределения».
- На основе фиксированного интервала времени: контролируется ZCollectionInterval, подходит для работы с внезапными сценариями трафика. Когда трафик изменяется плавно, адаптивный алгоритм может запускать сборку мусора, когда использование кучи превышает 95%. Когда трафик внезапно возрастает, время адаптивного алгоритма может срабатывать слишком поздно, что приводит к блокировке некоторых потоков. Регулируя этот параметр, мы решаем проблему сценариев всплесков трафика, таких как временные события, всплески и другие сценарии. Ключевое слово в журнале "Таймер".
- Активное правило срабатывания: аналогично правилу с фиксированным интервалом, но временной интервал не является фиксированным. Это время рассчитывается самой ZGC. Поскольку наш сервис добавил механизм срабатывания на основе фиксированного временного интервала, эта функция отключается через - Параметр ZProactive, позволяющий избежать частого GC, влияет на доступность службы. Ключевое слово в журнале - "Proactive".
- Правило прогрева: Появляется, когда служба только запущена, и обычно не требует внимания. Ключевое слово в журнале - "Разминка".
- Внешний триггер: триггер путем явного вызова System.gc() в коде. Ключевое слово в журнале — «System.gc()».
- Триггер распределения метаданных: вызвано недостаточной областью метаданных, как правило, не нужно беспокоиться. Ключевое слово в журнале — «Порог GC метаданных».
Понимание журналов ZGC
Полный процесс GC, точки, требующие внимания, отмечены на рисунке.
Примечание. Этот журнал фильтрует информацию, поступающую в защищенную точку. В нормальных условиях операция входа в безопасную точку также перемежается во время процесса GC.
Каждая строка в журнале GC указывает информацию во время процесса GC.Ключевая информация выглядит следующим образом:
- Start: Запустите сборщик мусора и укажите причину триггера сборщика мусора. Триггером на приведенном выше рисунке является адаптивный алгоритм.
- Phase-Pause Mark Start: Начальная метка будет STW.
- Phase-Pause Mark End: Марк еще раз, будет STW.
- Phase-Pause Relocate Start: Первоначальный перевод, будет STW.
- Информация о куче: записывает изменения размера кучи до и после пометки и перемещения во время процесса сборки мусора. High и Low записывают максимальное и минимальное значения. Обычно мы обращаем внимание на значение Used в High. Если оно достигает 100%, должно быть недостаточно памяти во время процесса GC. Необходимо настроить время запуска GC, раньше или раньше GC быстро.
- Информационная статистика ГК: вы можете регулярно печатать информацию о сборке мусора и просматривать всю статистику с момента запуска до настоящего времени в течение 10 секунд, 10 минут и 10 часов. Используя эту статистику, вы можете устранять неполадки и обнаруживать некоторые ненормальные точки.
В логе много контента, а ключевые моменты отмечены красными линиями. Смысл легко понять. Для более подробных объяснений вы можете проверить информацию в Интернете.
Поймите, почему ZGC глохнет
Во время реального боя мы обнаружили 6 сценариев, которые приводили к остановке программы, а именно:
- При GC начальная отметка: Пауза Отметить Начало в журнале.
- Когда GC, отметьте снова: Пауза Отметить конец в журнале.
- GC, первоначальный перевод: Пауза Relocate Start в журнале.
- блокировка выделения памяти: когда памяти недостаточно, поток блокируется и ждет завершения сборки мусора.Ключевое слово — «Останов выделения».
- Безопасная точка: сборка мусора может выполняться только после того, как все потоки войдут в безопасную точку ZGC периодически входит в безопасную точку, чтобы определить, требуется ли сборка мусора. Поток, который первым вошел в точку сохранения, должен ждать потока, который позже войдет в точку сохранения, пока все потоки не будут приостановлены.
- дамп потока, памяти: Например, команды jstack, jmap.
Тюнинг кейс
Поддерживаемый нами сервис называется Zeus. Это платформа правил Meituan, которая часто используется для управления правилами в сценариях контроля рисков. Выполнение правил основано на механизме выполнения выражений с открытым исходным кодом.Aviator. Aviator внутренне преобразует каждое выражение в класс Java и реализует логику выражения, вызывая интерфейс класса.
Количество правил в сервисе Zeus превышает 10 000, а на каждую машину приходится миллионы запросов в день. Эти объективные условия заставляют классы и методы, сгенерированные Aviator, генерировать много ClassLoader и CodeCache, которые стали узким местом производительности GC при использовании ZGC. Далее представлены два типа случаев настройки.
Выделение памяти заблокировано, и системная пауза может достигать второго уровня
Случай 1. Внезапное увеличение трафика во время события seckill привело к сбоям в работе.
информация журнала: Сравнивая журналы GC и бизнес-логи на момент сбоя производительности, было обнаружено, что JVM была приостановлена в течение длительного времени, и в журнале GC во время паузы имеется большое количество журналов «Allocation Stall».
анализировать: Такой случай чаще всего возникает в сценариях, где «адаптивный алгоритм» является основным механизмом запуска GC. ZGC — параллельный сборщик мусора.Потоки GC и потоки приложений активны одновременно.В процессе GC будут генерироваться новые объекты. Прежде чем сборка мусора будет завершена, вновь сгенерированные объекты заполнят кучу, поэтому поток приложения может заблокировать поток из-за невозможности обратиться за памятью. Когда началась пиковая активность, в систему поступило большое количество запросов, но интервал срабатывания сборщика мусора, рассчитанный адаптивным алгоритмом, был большим, что приводило к несвоевременному срабатыванию сборщика мусора, блокированию выделения памяти и возникновению паузы.
Решение:
(1) Включите механизм запуска сборщика мусора на основе «фиксированного интервала времени»: -XX:ZCollectionInterval. Например, настроить его на 5 секунд или даже меньше.
(2) Увеличьте поправочный коэффициент -XX:ZAllocationSpikeTolerance, чтобы запустить GC раньше. ZGC использует модель нормального распределения для прогнозирования скорости выделения памяти.Значение коэффициента коррекции модели ZAllocationSpikeTolerance по умолчанию равно 2. Чем больше значение, тем раньше срабатывает GC.Все кластеры в Zeus установлены на 5.
Случай 2: Во время стресс-теста, после постепенного увеличения трафика до определенного уровня, появился заусенец производительности
информация журнала: сборка мусора выполняется в среднем раз в 1 секунду, а интервала между двумя сборками почти нет.
анализировать: GC срабатывает вовремя, но скорость маркировки и очистки памяти слишком низкая, что приводит к блокировке выделения памяти и вызывает паузу.
Решение: увеличьте -XX:ConcGCThreads, чтобы ускорить одновременную маркировку и переработку. Значение ConcGCThreads по умолчанию — 1/8 от числа ядер, для 8-ядерных машин значение по умолчанию — 1. Этот параметр влияет на пропускную способность системы.Если интервал ГХ длиннее, чем цикл ГХ, настраивать этот параметр не рекомендуется.
Количество корней сборщика мусора велико, а время паузы одного сборщика мусора велико.
Случай 3: время паузы одного GC составляет 30 мс, что сильно отличается от ожидаемой паузы около 10 мс.
информация журнала: Наблюдать за статистикой информации лога ZGC Пункт "Pause Roots ClassLoaderDataGraph" занимает много времени.
анализировать: Сделайте дамп файла памяти и обнаружите, что в системе есть десятки тысяч экземпляров ClassLoader. Мы знаем, что ClassLoader является частью GC Roots, а время паузы ZGC пропорционально GC Roots, чем больше количество GC Roots, тем больше время паузы. При дальнейшем анализе имя класса ClassLoader показывает, что эти ClassLoader генерируются компонентом Aviator. При анализе исходного кода Aviator обнаруживается, что когда Aviator генерирует новый класс для каждого выражения, он создает ClassLoader, что приводит к проблеме огромного количества ClassLoaders. В более поздних версиях Aviator проблема была устранена путем создания только одного ClassLoader для создания классов для всех выражений.
Решение: Обновите версию компонента Aviator, чтобы избежать создания избыточных ClassLoaders.
Случай 4: после запуска службы, чем дольше время работы, тем дольше время одного GC и восстановление после перезапуска.
информация журнала: просмотрите статистику информации журнала ZGC.Время, затрачиваемое на "Pause Roots CodeCache", будет постепенно увеличиваться со временем работы службы.
анализировать: Пространство CodeCache используется для хранения результатов JIT-компиляции горячего кода Java, а CodeCache также является частью GC Roots. Добавив параметр -XX:+PrintCodeCacheOnCompilation, распечатайте оптимизированные методы в CodeCache и найдите большое количество кодов выражений Aviator. Чтобы найти первопричину, каждое выражение представляет собой метод в классе. По мере увеличения времени выполнения и увеличения числа выполнений эти методы будут скомпилированы в кэш кода путем JIT-оптимизации, что приведет к увеличению кэша кода.
Решение: JIT имеет некоторую конфигурацию параметров для настройки условий JIT-компиляции, но ни один из них не применим к нашей задаче. В конце концов мы решили эту проблему за счет бизнес-оптимизации, удалив выражения Aviator, которые не нужно выполнять, что позволило избежать попадания большого количества методов Aviator в CodeCache.
Стоит отметить, что мы не полностью развернули все кластеры после того, как все эти проблемы были решены. Даже если в начале есть различные глюки, после расчета оказывается, что ZGC с различными проблемами меньше влияет на доступность сервиса, чем предыдущая CMS. Таким образом, подготовка к использованию ZGC для полноценного развертывания заняла около 2 недель. В течение следующих 3 месяцев мы отслеживали эти проблемы, работая над бизнес-требованиями, и, наконец, решили вышеуказанные проблемы одну за другой, что позволило ZGC добиться лучшей производительности на каждом кластере.
Улучшить эффект ZGC
Снижение задержки
TP (верхний процентиль) — это показатель для измерения задержки системы: TP999 представляет собой минимальное время, необходимое для ответа на 99,9% запросов; TP99 представляет собой минимальное время, необходимое для ответа на 99% запросов.
Среди различных кластеров, обслуживаемых Zeus, ZGC более выгоден в сценариях с малой задержкой (TP999
- TP999: уменьшение 12~142 мс, диапазон уменьшения 18%~74%.
- TP99: уменьшение на 5~28 мс, диапазон уменьшения составляет 10%~47%.
Службы со сверхнизкой задержкой (TP999 200 мс) не имеют большого преимущества, поскольку узким местом времени отклика этих служб является не GC, а производительность, зависящая от внешних факторов.
Падение пропускной способности
Для сценариев с приоритетом пропускной способности ZGC может не подойти. Например, автономный кластер в Zeus изначально использовал CMS, после обновления ZGC пропускная способность системы значительно снизилась. Этому есть две причины: во-первых, ZGC — сборщик мусора одного поколения, а CMS — сборщик мусора поколений. Сборщики мусора одного поколения каждый раз обрабатывают больше объектов, что потребляет больше ресурсов ЦП; во-вторых, ZGC использует барьеры чтения, а операции с барьерами чтения требуют дополнительных вычислительных ресурсов.
Суммировать
Как сборщик мусора следующего поколения, ZGC обладает отличной производительностью. Процесс сборки мусора ZGC почти полностью выполняется параллельно, а фактическое время паузы STW чрезвычайно короткое, менее 10 мс. Это связано с его цветным указателем и технологией барьера чтения.
В процессе обновления JDK 11+ZGC компания Zeus классифицировала риски и проблемы, а затем решала их по отдельности и, наконец, успешно достигла цели обновления, а паузы GC почти больше не влияют на доступность системы.
Наконец, я рекомендую всем обновить ZGC, поскольку система Zeus столкнулась со многими проблемами из-за своих бизнес-характеристик, и другие команды по управлению рисками прошли очень гладко во время обновления. Приглашаем всех присоединиться к группе «Использование и обмен ZGC».
использованная литература
- официальный сайт ЗГК
- Ченхан Пэн, «Проектирование и внедрение нового поколения мусоросборников ZGC», Machinery Industry Press, 2019.
- Разговор об оптимизации GC Java-приложений из практических кейсов
- Некоторые ключевые технологии Java Hotspot G1 GC
приложение
Как использовать новые технологии
При обновлении JDK 11 и использовании ZGC в продакшене всех больше всего волнует не эффект, а то, что этой новой версией мало кто пользуется, мало онлайн-практик, она ненадежна и стабильна. Во-вторых, будет ли стоимость обновления очень большой.Если оно будет неудачным, это не будет пустой тратой времени. Итак, прежде чем использовать новую технологию, первое, что нужно сделать, это оценить выгоды, затраты и риски.
Оцените преимущества
Для таких программ, как JDK, на которые обращает внимание весь мир, новые технологии, представленные в основных обновлениях версий, как правило, теоретически проверены. Что нам нужно сделать, так это определить, является ли узкое место текущей системы проблемой, которую можно решить с помощью новой версии JDK, и мы не должны принимать меры без диагностики проблемы. Оценив преимущества, оцените затраты и риски: если преимущества слишком велики или слишком малы, два других веса воздействия будут намного меньше.
Взяв в качестве примера случай, упомянутый в начале этой статьи, предполагается, что количество GC остается неизменным (10 раз в минуту), а время одного GC уменьшается с 40 мс на 10 мс. По расчетам, 100/60000 = 0,17% времени GC выполняется за одну минуту, и все запросы приостанавливаются только на 10 мс в течение периода GC.Количество запросов, затронутых во время GC, и задержка из-за увеличения GC уменьшаются.
Оценить стоимость
В основном это относится к трудозатратам, необходимым для модернизации. Этот элемент является относительно зрелым, и точки изменения оцениваются в соответствии с руководством пользователя новой технологии. Это не сильно отличается от других проектов, поэтому я не буду вдаваться в подробности.
В нашей практике онлайн-развертывание завершается в течение двух недель, достигая состояния безопасной и стабильной работы. Последующие итерации продолжались в течение 3 месяцев, и ZGC оптимизировался и адаптировался в большей степени в соответствии с бизнес-сценариями.
оценить риск
Риски обновления JDK можно разделить на три категории:
- Риск совместимости: JAR-пакеты Java-программы во многом зависят от того, сможет ли программа работать после обновления версии JDK. Например, наш сервис обновлен с JDK 7 до JDK 11, и необходимо решить многие проблемы несовместимости пакетов JAR.
- функциональный риск: Будут ли после запуска какие-то изменения логики компонентов, влияющие на логику существующих функций.
- риск производительности: Если с функцией проблем нет, то производительность стабильна и может стабильно работать в сети.
После классификации ответ на каждый тип риска был преобразован в общую тестовую задачу и больше не является неизвестным риском. Риск относится к неопределенным вещам, если неопределенные вещи могут быть преобразованы в определяемые вещи, это означает, что риск был устранен.
Обновите JDK 11
JDK 11 выбран потому, что ZGC впервые поддерживается в JDK 11, а JDK 11 — это версия с долгосрочной поддержкой (LTS), которая будет поддерживаться не менее трех лет.Обычные версии (такие как JDK 12, JDK 13 и JDK 14) имеют только 6-месячный цикл обслуживания, что не рекомендуется.
Установка локальной тестовой среды
из двух источниковOpenJDKиOracleJDKЗагрузите JDK 11. Основное различие между двумя версиями JDK заключается в том, что она бесплатная и платная в течение длительного времени, и обе бесплатны в краткосрочной перспективе. Обратите внимание, что ZGC в версии JDK 11 не поддерживает системы Mac OS.При использовании JDK 11 в системах Mac OS можно использовать только другие сборщики мусора, такие как G1.
Установка производственной среды
Обновление JDK 11 — это не только обновление версии JDK вашего собственного проекта, но также требуется поддержка проекта, такая как инструменты компиляции, выпуска и развертывания, запуска, мониторинга и анализа производительности памяти. Практика в Meituan:
Скомпилировать и упаковать: Система выпуска Meituan поддерживает выбор JDK 11 для компиляции и упаковки.Онлайн-операция и полное развертывание: требуется, чтобы на онлайн-машине был установлен JDK11, есть 3 способа:
1. Новая подача заявки на виртуальную машину с установленным по умолчанию JDK 11: этот метод можно использовать при попытке использовать JDK 11; во время полного развертывания, если будет слишком много новых примененных машин, может не хватить машинных ресурсов. 2. Установите JDK 11 на существующие виртуальные машины с помощью рукописных скриптов: не рекомендуется, студенты-бизнесмены слишком заняты эксплуатацией и обслуживанием. 3. Используйте функцию развертывания образа, предоставляемую контейнером, для установки JDK 11 при упаковке образа: рекомендуемый метод не требует новых ресурсов приложения.
Показатели мониторинга: В основном время и частота GC, мы поддерживаем сбор данных ZGC через систему мониторинга Meituan CAT (CAT имеет открытый исходный код).Анализ производительности памяти: Когда вы сталкиваетесь с проблемами производительности в Интернете, вам также необходимо использовать инструменты профилирования.Платформа диагностики и оптимизации производительности Meituan Scalpel поддерживает анализ производительности памяти JDK 11. Если в вашей компании нет соответствующих инструментов, рекомендуется использовать JProfier.
Совместимость компонентов
Наш проект содержит более 200 000 строк кода, его необходимо обновить с JDK 7 до JDK 11, и он зависит от многих компонентов. Хотя может показаться, что обновление будет более сложным, на самом деле решение проблемы совместимости заняло всего два дня. Конкретный процесс выглядит следующим образом:
1. Для компиляции вам необходимо изменить конфигурацию сборки в файле pom и изменить ее в соответствии с отчетом об ошибках.Существует два основных типа:
А. Некоторые классы удалены: например, "sun.misc.BASE64Encoder", найдите класс замены java.util.Base64.
б) Версии зависимостей компонентов несовместимы с JDK 11: найдите соответствующие зависимые компоненты, найдите последнюю версию и в целом поддерживайте JDK 11.
2. После успешной компиляции запустите и запустите.В это время все еще может быть проблема версии зависимости компонента, которая может быть обработана в соответствии с методом во время компиляции.
Зависимости, измененные обновлением:
<dependency>
<groupId>javax.annotation</groupId>
<artifactId>javax.annotation-api</artifactId>
<version>1.3.2</version>
</dependency>
<dependency>
<groupId>javax.validation</groupId>
<artifactId>validation-api</artifactId>
<version>2.0.1.Final</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.4</version>
</dependency>
<dependency>
<groupId>org.hibernate.validator</groupId>
<artifactId>hibernate-validator-parent</artifactId>
<version>6.0.16.Final</version>
</dependency>
<dependency>
<groupId>com.sankuai.inf</groupId>
<artifactId>patriot-sdk</artifactId>
<version>1.2.1</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.9</version>
</dependency>
<dependency>
<groupId>commons-lang</groupId>
<artifactId>commons-lang</artifactId>
<version>2.6</version>
</dependency>
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-all</artifactId>
<version>4.1.39.Final</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
JDK 11 существует уже два года, и общие зависимые компоненты имеют совместимые версии. Однако, если это компонент корпоративного уровня, предоставляемый компанией, он может быть несовместим с JDK 11, и необходимо принудительно обновить соответствующие компоненты. Если обновление другой стороны затруднено, вы можете разделить функции, развернуть функции, зависящие от этих компонентов, по отдельности и продолжать использовать более раннюю версию JDK. Поскольку всем известна отличная производительность JDK11, считается, что больше команд будут использовать JDK11 для решения проблемы GC.Чем больше пользователей, тем выше мотивация для обновления каждого компонента.
Проверить функциональную правильность
Функциональная корректность гарантируется полным модульным тестированием, интеграционным и регрессионным тестированием.
об авторе
- Ван Донг, старший инженер Meituan Information Security.
- Ван Вэй, эксперт по технологиям информационной безопасности Meituan.
Чтобы прочитать больше технических статей, отсканируйте код, чтобы подписаться на общедоступную учетную запись WeChat — техническая команда Meituan!