Введение
Сборка мусора Shenandoah OpenJDKОсновная мотивация проекта (GC) — сократить время паузы при сборке мусора. В JDK 12 был выпущен оригинальный сборщик мусора Shenandoah, который реализует параллельную эвакуацию кучи, решая основную проблему очистки (большой) кучи без остановки приложения. В конечном итоге этот выпуск был перенесен на JDK 11. В JDK 14 мы реализовалиПараллельная разгрузка класса, в JDK 16 мы добавилиПараллельная обработка ссылок, которые еще больше сокращают время паузы операций по сборке мусора. Остальные операции по сборке мусора под паузой — это обработка стека потоков, и мы имеемВ JDK 17решил эту проблему.
В этой статье описывается новая обработка стека параллельных потоков в Shenandoah GC. Параллельная обработка стеков потоков в JDK 17 дала нам надежные паузы менее миллисекунды.
2. Обработка потоков в Java
Что такое многопоточность и почему нам нужно останавливать приложение для этого? Java-программы выполняются в потоках, и каждый поток имеет _stack_: список кадров стека, каждый из которых содержит локальные переменные, мониторы и другую информацию, связанную с текущим выполняемым методом. Что наиболее важно, в контексте сборки мусора Java он содержит ссылки на объекты кучи (например, локальные переменные, которые ссылаются на типизированные объекты).
Когда начинается цикл сборки мусора, мы сначала сканируем стеки всех потоков, чтобы заполнить очередь маркеров ссылками, которые мы находим в стеке. Мы делаем это на паузах GC (безопасных точках), потому что нам нужно согласованное состояние стека в начале маркера, а не выполнение потоков, одновременно воздействующих на стек. Как только это будет сделано, мы пройдемся по графу доступных объектов, начиная со ссылок, которые мы нашли во время начального сканирования потока.
Точно так же при эвакуации доступного объекта в пустую область нам необходимо обновить все ссылки в стеке потоков, чтобы они указывали на новое местоположение объекта. Нам нужно сделать небольшую паузу, потому что барьеры загрузки сборки мусора обычно работают при загрузке ссылок из кучи (например, в локальные переменные или регистры), а это означает, что локальные переменные или регистры не могут мешать ссылкам на объекты в состоянии, требующем GC. К тому времени уже слишком поздно преодолевать барьер сбора мусора. Быстрый вызов барьеров сборки мусора для каждой локальной переменной или доступа к регистру приводит к проблемам с производительностью.
Сканирование и обработка стеков потоков требует времени. Небольшие рабочие нагрузки (несколько потоков с небольшими стеками) могут занимать всего миллисекунды для сканирования, но большие рабочие нагрузки — серверы приложений, я смотрю на вас! - легко обрабатывается за десятки миллисекунд. Вся эта обработка выполняется, когда приложение остановлено, поэтому она влияет на общую сквозную задержку приложения.
3. Параллельная обработка потоков в OpenJDK 17
Как мы можем улучшить эту ситуацию и одновременно обрабатывать стеки потоков? Мы используем метод, называемыйстек водяных знаковмеханизм (изначально реализованный разработчиками ZGC) для достижения этой цели. Основное наблюдение заключается в том, что все операции стека потоков происходят в самом верхнем кадре: в текущем выполняемом методе. Все кадры ниже в основном статичны и не изменяются — они могут одновременно сканироваться сборщиком мусора потокобезопасно. Все, что нам нужно сделать, это согласовать поток GC с исполняющим потоком, когда кадр стека уничтожен (например, путем возврата к вызывающей стороне или путем выдачи исключения), чтобы выйти из процесса GC. Эта координация достигается с помощью водяного знака стека, указателя, который сообщает нам, какие части стека безопасны для сканирования, и барьера, который позволяет сборщику мусора обрабатывать возврат.
4. Используйте водяные знаки стека во время сборки мусора
Рассмотрим пример. Скажем, в начале разметки, во время начальной паузы, мы устанавливаем водяной знак стека на самый верхний кадр каждого потока и ставим поток на охрану. Это означает, что мы считаем все кадры безопасными для одновременного сканирования, но ни один из них (пока) не может быть выполнен. После возврата из безопасной точки мы передаем управление Java-программе, поэтому для безопасного выполнения требуется фреймворк верхнего уровня. Здесь в игру вступает барьер водяных знаков стека, позволяющий сборщику мусора обрабатывать верхний фрейм (и его вызывающую программу по практическим соображениям). Поток будет сканировать верхний кадр и соответствующим образом снизит водяной знак, а затем возобновит собственное выполнение в точке, которую он оставил перед безопасной точкой. При этом поток GC тоже начинает сканировать стек снизу вверх до водяного знака, т.е. в безопасной области.
- Уменьшает водяной знак на один кадр.
- Предотвращает сканирование потоков GC за пределами этого водяного знака.
- Обработайте кадр над водяным знаком, отсканировав все ссылки.
Конечным результатом является то, что мы будем эффективно сканировать все те же кадры и ссылки, что и при паузе начального маркера, но мы делаем это одновременно во время выполнения программы.
5. Бенчмарк Shenandoah GC
Итак, какое влияние эти изменения имеют на практике? Я провел несколько тестов, измеряющих паузы при сборке мусора. В таблице ниже показано среднее время пауз во всех тестах JDK 11, JDK 16 и JDK 17. Различия между JDK 16 и JDK 17 показывают улучшения, достигнутые за счет параллельной обработки стека. Отличия от JDK 11 показаны для полноты и включают различные другие улучшения по сравнению с предыдущими версиями.
начальная отметка | окончательная оценка | |
---|---|---|
JDK 11 | 421 микросекунда | 1294 микросекунды |
JDK 16 | 321 микросекунда | 704 микросекунды |
JDK 17 | 63 микросекунды | 328 микросекунд |
6. Релизное издание
Доступность Shenandoah зависит от поставщика и версии JDK. Сборки OpenJDK 12+ обычно включают Shenandoah по умолчанию. OpenJDK 11 требует подписки во время сборки.
Статус известного поставщика:
- Красная шляпа
- Выпуски Fedora 24+ OpenJDK 8+ включают Shenandoah
- RHEL 7.4+ поставляется с OpenJDK 8+, который включаетпредварительный просмотр технологииShenandoah
- Версия Red Hat OpenJDK 8u для WindowsВ том числе Шенандоа
- Амазонка
- Shenandoah доступен в Amazon Corretto, начиная с OpenJDK 11.0.9.
- Oracle
- не вВыпускайте Shenandoah в любой версии, включая сборки OpenJDK и проприетарные сборки.
- Azul
- Shenandoah выпускается на Azul Zulu, начиная с OpenJDK 11.0.9.
- OpenJDK
- Shenandoah доступен как бинарный файл по умолчанию, начиная с OpenJDK 11.0.9.
- дистрибутив Linux
- Debian выпускает Shenandoah, начиная с OpenJDK 11.0.9
- ebuild для Gentoo от IcedTea имеетЛоготип USE Шенандоа
- В дистрибутивах на основе RHEL/Fedora или других дистрибутивах, использующих их пакеты, также может быть включен Shenandoah. Стоит отметить, что общеизвестно, что CentOS,Oracle Linuxа такжеAmazon Linuxоба опубликовали его.
7. Включить Шенандоа
использовать-XX:+UseShenandoahGCОпция JVM для запуска вашего Java-приложения с помощью Shenandoah GC.
java <PATH_TO_YOUR_APPLICATION> -XX:+UseShenandoahGC
7.1 Режим
Режим определяет основной способ работы Шенандоа. Это определяет барьеры (если таковые имеются) для использования Shenandoah и определяет основные характеристики производительности. можно использовать-XX:ShenandoahGCMode=Выберите режим. Доступные режимы:
- normal/satb(По умолчанию). В этом режиме выполняются одновременные сборщики мусора с маркировкой Snapshot-At-The-Beginning (SATB). Этот режим маркировки аналогичен тому, что делает G1: перехват записи и маркировки через «предыдущий» объект.
- iu(экспериментальный). В этом режиме выполняются одновременные сборщики мусора с маркировкой добавочного обновления (IU). Этот шаблон маркировки является зеркальным отражением шаблона SATB: записи и маркировки перехватываются «новыми» объектами. Это может сделать разметку менее консервативной, особенно когда речь идет о доступе к слабым ссылкам.
- passive(диагноз). В этом режиме работает сборщик мусора «останови мир». Этот режим используется для функционального тестирования, но иногда он полезен для разделения пополам исключений производительности с помощью барьеров GC или для расчета фактических объемов данных в реальном времени в приложениях.
7.2 Базовая конфигурация
Базовая конфигурация и параметры командной строки:
- -Xlog:gc(начиная с JDK 9) или-verbose:gc(до JDK 8) будет печатать отдельные тайминги сборщика мусора.
- -Xlog:gc+ergo(начиная с JDK 9) или-XX:+PrintGCDetails(до JDK 8) или будет печатать эвристические решения, которые могут выявить выбросы (если таковые имеются).
- -Xlog:gc+stats(начиная с JDK 9) или-verbose:gc(до JDK 8) распечатает сводную таблицу о внутренних таймингах Шенандоа в конце прогона.
Почти всегда рекомендуется запускать с включенным ведением журнала. Эта сводная таблица содержит важную информацию о производительности GC, которую мы почти неизбежно запрашиваем в отчетах об ошибках производительности. Эвристическое ведение журнала полезно для поиска выбросов GC.
Другие рекомендуемые параметры JVM:
- -XX:+AlwaysPreTouch: Фиксация страниц кучи в памяти помогает сократить количество прерываний с задержкой.
- -Xmsа также-XMX:использовать-Xms = -XmxСделайте размер кучи неизменяемым, что уменьшит проблемы с управлением кучей. комбинироватьAlwaysPreTouch,-Xms = -XmxВся память будет выделена при запуске, что позволит избежать проблем, когда память в конечном итоге будет использована.-Xmsтакже определяет нижнюю границу незанятой памяти, поэтому используйте-Xms = -XMX,Вся память останется зафиксированной. Тем не менее, если вы хотите настроить Shenandoah для уменьшения занимаемой площади, рекомендуется установить более низкое значение.-Xms. Вам нужно решить, насколько низко установить это значение, чтобы сбалансировать накладные расходы на фиксацию/отмену фиксации с объемом памяти. Во многих случаях установка-XmsЛюбой низкий уровень подходит.
- Использование огромных страниц значительно повышает производительность больших куч. Есть два способа подписаться.-XX:+UseLargePagesПоддержка Hugetlbfs (Linux) или Windows (с соответствующими разрешениями) будет включена.-XX:+UseTransparentHugePagesпозволит это прозрачно. Для прозрачных огромных страниц рекомендуется/sys/kernel/mm/transparent_hugepage/enabledа также/sys/kernel/mm/transparent_hugepage/дефрагментация настроекза"madvise". При работе с AlwaysPreTouch он также прогревается при запуске, чтобы уменьшить нагрузку на дефрагментацию.
- -XX:+UseNUMA: хотя Shenandoah еще не поддерживает NUMA явно, было бы неплохо включить его, чтобы включить чередование NUMA на хостах с несколькими сокетами. В сочетании с AlwaysPreTouch он обеспечивает лучшую производительность, чем готовая конфигурация по умолчанию.
- -XX:-UseBiasedLocking: существует компромисс между пропускной способностью неоспариваемых (предвзятых) блокировок и безопасной точкой для JVM, позволяющей включать и отключать их по мере необходимости. Для рабочих нагрузок, ориентированных на задержку, имеет смысл отключить предвзятую блокировку.
- -XX:+DisableExplicitGC:Вызов System.gc() из пользовательского кода вынуждает Shenandoah выполнять дополнительный цикл GC; отключение его для предотвращения злоупотребления кодом System.gc() может быть полезным. Обычно не поражается, потому что-XX:+ExplicitGCInvokesConcurrentВключено по умолчанию, что означает, что параллельные циклы GC будут вызываться вместо STW Full GC.
8. Заключение
В этой статье объясняется, как параллельная обработка стека потоков в Shenandoah GC решает проблему остаточного времени паузы сборки мусора и обеспечивает надежные паузы сборки мусора менее миллисекунды в JDK 17. Чтобы узнать больше, посетите сайт проекта Shenandoah GC.Репозиторий GitHubа такжеВики-страница OpenJDK.
9. Переводчик сказал
На основе исходного текста мы добавили сравнение отношенияКак использовать шестой и седьмой разделысвязанная информация. Авторские компоненты облачного микросервиса SpringmicaТак же был адаптирован под java17, и выпущен пров mybatis-plus. В ближайшем будущем выйдет несколько статей по java17, подписывайтесь на меня! ! !
Оригинальная ссылка:Developers.RedHat.com/articles/20…