Сборщики мусора: часть 1. Обзор
Сборка мусора — фоновый процесс в JVM, который удаляет уже ненужные объекты. В основном каждая реализация балансирует между двумя параметрами:
🔹Latency: максимальное время обработки запроса
🔹Пропускная способность: сколько запросов обработается в минуту
"Запрос" здесь - взаимодействие приложения с памятью: создание, обновление и удаление объектов.
Приоритет зависит от приложения:
▪️Для веб-сервисов большие задержки недопустимы. Лучше в среднем ответ будет на четверть секунды дольше, но не будет подвисших запросов.
▪️Приложение работает в фоновом режиме. Например, архивирует записи. Не критично, если каждая сотая запись будет архивироваться 5 секунд, если за час обработается больше данных.
Алгоритм коллектора везде один и тот же:
🔸Отмечаем объекты, которые нужны
🔸Помечаем остальные объекты на удаление
🔸Удаляем ненужные объекты
🔸Группируем выжившие
Чтобы понять, какие объекты нужны приложению, строится граф достижимых объектов. Самостоятельные объекты называются roots и существуют без привязки к другим. Это локальные и статические переменные, потоки и т.д.
Смотрим, на какие объекты ссылаются roots. Идём дальше по ссылкам и получаем граф живых объектов.
⚠️Но есть проблема: объекты в памяти постоянно меняются. Есть две крайности:
1️⃣ Когда памяти останется мало, целиком остановить приложение и убрать мусор. Будет большая пауза, но в остальное время сборщик не будет тормозить основное приложение.
2️⃣ Следить за статусом объектов и ссылок, вести таблицы использования. Так ненужные объекты легко найти, но сам сборщик будет занимать много процессорного времени.
Разные коллекторы балансируют между этими крайностями: некоторые фазы происходят параллельно, а для некоторых нужна полная остановка.
Универсального сборщика нет, на выбор влияет количество процессоров, памяти, тип нагрузки и так далее.
В части 2 мы подробно рассмотрим сборщики мусора в java 8
#jvm
Сборка мусора — фоновый процесс в 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
В команде 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
В 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