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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

#jvm
👍3❤1
Сборщики мусора, часть 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