Java: fill the gaps
12.9K subscribers
7 photos
215 links
Привет! Меня зовут Диана, и я занимаюсь разработкой с 2013. Здесь пишу просто и понятно про джава бэк

🔥Тот самый курс по многопочке🔥
https://fillthegaps.ru/mt

Комплименты, вопросы, предложения: @utki_letyat
Download Telegram
Сборщики мусора: часть 1. Обзор

Сборка мусора — фоновый процесс в JVM, который удаляет уже ненужные объекты. В основном каждая реализация балансирует между двумя параметрами:

🔹Latency: максимальное время обработки запроса
🔹Пропускная способность: сколько запросов обработается в минуту

"Запрос" здесь - взаимодействие приложения с памятью: создание, обновление и удаление объектов.

Приоритет зависит от приложения:
▪️Для веб-сервисов большие задержки недопустимы. Лучше в среднем ответ будет на четверть секунды дольше, но не будет подвисших запросов.

▪️Приложение работает в фоновом режиме. Например, архивирует записи. Не критично, если каждая сотая запись будет архивироваться 5 секунд, если за час обработается больше данных.

Алгоритм коллектора везде один и тот же:
🔸Отмечаем объекты, которые нужны
🔸Помечаем остальные объекты на удаление
🔸Удаляем ненужные объекты
🔸Группируем выжившие

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

Смотрим, на какие объекты ссылаются roots. Идём дальше по ссылкам и получаем граф живых объектов.

⚠️Но есть проблема: объекты в памяти постоянно меняются. Есть две крайности:
1️⃣ Когда памяти останется мало, целиком остановить приложение и убрать мусор. Будет большая пауза, но в остальное время сборщик не будет тормозить основное приложение.
2️⃣ Следить за статусом объектов и ссылок, вести таблицы использования. Так ненужные объекты легко найти, но сам сборщик будет занимать много процессорного времени.

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

Универсального сборщика нет, на выбор влияет количество процессоров, памяти, тип нагрузки и так далее.

В части 2 мы подробно рассмотрим сборщики мусора в java 8

#jvm
👍31
Сборщики мусора, часть 2. Java 8

В команде java 8 четыре участника: Serial GC, Parallel GC, CMS, G1.

Прежде, чем разобрать разницу между ними, повторим теорию. Базовый алгоритм сборки мусора такой:
🔸Построить граф живых объектов
🔸Пометить те, что остались, на удаление
🔸Удалить ненужные объекты
🔸Сгруппировать выжившие
*Сборщики в java 8 пропускают последнюю фазу

Обходить каждый раз всю память - долго и нерационально. Выгоднее всего убирать локальные переменные и промежуточные объекты. Это суть гипотезы поколений:

Память делится на регионы. Новые объекты помещаются в область «новое поколение». Там сборщик будет работать чаще всего. Объекты, которые пережили несколько сборок, считаются «старыми» и переносятся в другую область памяти. Их проверяют реже.

Сборка может быть трёх типов:
▫️Minor - обойти только молодое поколение
▫️Major - только старое
▫️Full - память целиком

Перейдём к практике. Я опустила 90% деталей, чтобы наглядно показать разницу между коллекторами.

Serial GC
При любом типе сборки коллектор останавливает потоки основного приложения. Один поток коллектора выполняет все этапы.
Минимум действий при работе приложения
Долгие паузы
Когда ок: память до 100 МБ, одно ядро, остановки не критичны

Parallel GC
Тоже останавливает работу приложения, но запускает несколько потоков для сборки мусора. Сборщик по умолчанию в java 8.
Работает почти незаметно — не более 1% процессорного времени
Чем больше памяти — тем дольше паузы.

CMS - Concurrent Mark Sweep
Старое и новое поколение обрабатывается по-разному.
▫️Чистка нового поколения как в Parallel GC: основное приложение останавливается, и запускаются несколько потоков коллектора.
▫️Старшее поколение собирается по-другому:
Большая часть графа объектов строится параллельно с основным приложением. Объекты удаляются тоже параллельно. Поэтому сборщик и называется Concurrent Mark Sweep - параллельная разметка и удаление.
⛔️ Помечен Deprecated в Java 9, потому что по всем фронтам уступает G1

G1 - Garbage First
Предыдущие сборщики делили память на 3-4 большие области — одно для старого поколения и 2-3 для нового. Суть G1 — деление памяти на 2048 регионов, которым присваивается тип - свободный, young, survivor или old.

Что это даёт:
▪️Сборщик обрабатывает только несколько регионов, а не всё поколение за раз.
▪️Можно менять количество регионов и регулировать время паузы.
▪️Приложения с большой памятью не тормозят, тк память обрабатывается по частям.
▪️Если регион однородный, можно сразу поменять его тип. Был молодой — стал свободный. Был молодой — стал выживший.
▪️Каждый регион мониторится отдельно. Так G1 выбирает перспективные регионы, куда недавно добавилось много объектов. Отсюда и название - Garbage First.

В остальном G1 похож на предыдущие сборщики:
🔹Уборка в молодом поколении останавливает потоки приложения.
🔹Общая проходит как у CMS — некоторые фазы тормозят работу приложения, некоторые работают параллельно

Есть настройка длины паузы и других параметров. Гайд по тюнингу G1
Работает с большой памятью
Анализирует статистику сборок, хорошо работает с настройками по умолчанию.
Занимает до 10% процессорного времени

G1 показывает отличные результаты в энтерпрайзе. В java 9 стал сборщиком мусора по умолчанию.

В следующей части посмотрим на новые сборщики — Shenandoah, ZGC и Epsilon GC.

#jvm
1
Сборщики мусора, часть 3. Java 15

В java 15 в продакшн стадию вошли 2 новых сборщика мусора — Shenandoah и ZGC.

Они подойдут для приложений с памятью до 4 ТБ, где нежелательны длинные задержки. Среднее время паузы не превышает 1 миллисекунду. За такие короткие паузы придётся платить накладными расходами — сборщики занимают до 15% процессорного времени.

Как они работают и как получаются такие результаты?

Как и в прошлой статье, пропустим 90% деталей и посмотрим на главное. У новых коллекторов 2 особенности:

1️⃣ Расширенные ссылки
Обычно в ссылках на объект находится только адрес памяти, где этот объект лежит. Размер поля под ссылку — 64 бита, поэтому в теории можно хранить адрес в пределах 128 ТБ.

Это много, поэтому диапазон адресов сокращён до 4 ТБ, а в свободные биты коллектор записывает нужную для себя информацию. Ссылки читаются и обновляются атомарно. Сборщику нужно меньше доп.структур и синхронизации и можно работать почти параллельно с основным приложением.

2️⃣ Load barriers
При загрузке объекта сначала читается дополнительная информация из ссылки. Поток приложения или коллектора что-нибудь делает и только потом возвращает адрес в памяти.

Что еще. Оба сборщика:
Не используют поколения, работают со всеми объектами
Делят память на множество регионов
Группируют объекты. Дефрагментация помогает быстрее работать с памятью

Теперь о разнице между Shenandoah и ZGC:

Главное отличие в том, что конкретно записывается в ссылку, и как коллектор с этой информацией работает:

🔹Указатели в Shenandoah используют концепт brook pointers. В ссылке записан адрес объекта и, если объект был перемещён, новый адрес.

Допустим, есть объект Х, ссылки на который есть у десятка других объектов. При перемещении Х ссылки станут недействительны и должны быть обновлены. Но поскольку в brook pointers помещается два адреса, старые ссылки можно сразу не трогать. При чтении объекта поток увидит, что есть новая ссылка и сам перезапишет её.

🔹В ZGC у ссылок другой формат – colored pointers. В них коллектор записывает служебную информацию: был ли уже посещён объект, нужно ли его удалить или переместить, есть ли у него финалайзер и т.д

Ещё одно отличие — поддержка в JDK. Она зависит от производителя JDK, версии java и ОС.

🔸Shenandoah разрабатывает компания Red Hat. Сборщик доступен в OpenJDK для java 15, сделана поддержка для java 8(!!!) и 11. Oracle отказалась поддерживать Shenandoah, поэтому в OracleJDK его нет.

🔸Разработку ZGC ведёт Oracle. Коллектор включен везде: и в OracleJDK и OpenJDK. В 11 версии есть поддержка Linux, в 14 — Windows.

Epsilon GC
С java 11 доступен ещё один экспериментальный сборщик —Epsilon GC. С ним приложение работает максимально быстро, потому что сборки мусора как таковой нет. Заканчивается память — JVM завершает работу.

Где пригодится:
Короткие разовые задачи
Быстро сделал — быстро завершился
Тестирование и эксперименты
Без фоновой работы коллектора легче оценить производительность и занимаемую память.

#jvm
1