Сборка мусора в Java является одной из продвинутых тем. Знание сборки мусора в Java помогает нам настроить производительность выполнения нашего приложения.
Сборка мусора в Java
- В Java программистам не нужно заботиться о уничтожении объектов, которые больше не используются. Об этом заботится сборщик мусора.
- Сборщик мусора является фоновым потоком-демоном. По сути, он освобождает память кучи, уничтожая недостижимые объекты.
- Недостижимые объекты – это те, на которые больше нет ссылок ни в одной части программы.
- Мы можем выбрать сборщик мусора для нашей программы на Java через опции JVM, о которых мы рассмотрим позже в этом руководстве.
Как работает автоматическая сборка мусора?
Автоматическая сборка мусора – это процесс анализа кучи памяти, идентификации (также известной как “маркировка”) недоступных объектов и их уничтожения с компактизацией. Проблема этого подхода заключается в том, что с увеличением числа объектов время сборки мусора продолжает расти, поскольку ему нужно пройти через весь список объектов, искать недоступный объект. Однако эмпирический анализ приложений показывает, что большинство объектов имеют короткое время жизни. Это поведение было использовано для улучшения производительности JVM, и принятая методология обычно называется Генерационной сборкой мусора. В этом методе пространство кучи делится на поколения, такие как молодое поколение, старое или зрелое поколение и постоянное поколение. Пространство молодого поколения кучи – это новое пространство, где создаются все новые объекты. Как только оно заполняется, происходит минорная сборка мусора (также известная как минорная GC). Это означает, что все мертвые объекты из этого поколения уничтожаются. Этот процесс быстр, потому что, как видно из графика, большинство из них будут мертвы. Выжившие объекты в молодом поколении стареют и в конечном итоге перемещаются в старшие поколения. Старшее поколение используется для хранения долгоживущих объектов. Обычно устанавливается порог для объектов молодого поколения, и когда этот возраст достигается, объект перемещается в старшее поколение. В конечном итоге старшее поколение нужно собрать. Это событие называется основной сборкой мусора (major garbage collection). Часто это происходит намного медленнее, потому что включает в себя все живые объекты. Также существует полная сборка мусора (Full GC), что означает очистку всей кучи – как молодого, так и старшего поколений. Наконец, до Java 7 существовало постоянное поколение (или Perm Gen), которое содержало метаданные, необходимые JVM для описания классов и методов, используемых в приложении. Оно было удалено в Java 8.
Коллекторы мусора Java
На самом деле в JVM представлены четыре различных коллектора мусора, все они являются поколенческими. У каждого из них есть свои преимущества и недостатки. Выбор коллектора мусора зависит от нас, и может быть драматические различия в пропускной способности и паузах в приложении. Все они разделяют управляемую кучу на различные сегменты, используя давние предположения о том, что большинство объектов в куче имеют короткий срок службы и должны быстро перерабатываться. Итак, четыре типа сборщиков мусора:
Serial GC
Это самый простой сборщик мусора, разработанный для однопоточных систем и небольших размеров кучи. Замораживает все приложения во время работы. Может быть включен с использованием опции JVM -XX:+UseSerialGC
.
Параллельный/Пропускной GC
Это коллектор по умолчанию JVM в JDK 8. Как следует из названия, он использует несколько потоков для сканирования области кучи и выполнения компактизации. Недостатком этого коллектора является то, что он останавливает потоки приложения во время выполнения минорного или полного сборки мусора. Он наиболее подходит для приложений, которые могут обрабатывать такие паузы и пытаются оптимизировать нагрузку на ЦП, вызванную коллектором.
Коллектор CMS
Алгоритм коллектора CMS (“concurrent-mark-sweep”) использует несколько потоков (“concurrent”) для сканирования кучи (“mark”) в поисках неиспользуемых объектов, которые могут быть переработаны (“sweep”). Этот коллектор переходит в режим Stop-The-World(STW) в двух случаях: – Во время инициализации начальной маркировки корней, т.е. объектов в старом поколении, которые достижимы из точек входа потоков или статических переменных – Когда приложение изменило состояние кучи во время одновременного выполнения алгоритма и заставило его вернуться и выполнить некоторые заключительные операции, чтобы убедиться, что отмечены правильные объекты. У этого коллектора может возникнуть сбой продвижения. Если некоторые объекты из молодого поколения должны быть перемещены в старое поколение, и коллектору не удалось выделить достаточно места в области старого поколения, произойдет сбой продвижения. Чтобы предотвратить это, мы можем выделить больше памяти для старого поколения или предоставить коллектору больше фоновых потоков.
Сборщик G1
Последним, но не менее важным является сборщик Garbage-First, разработанный для размеров кучи более 4 ГБ. Он делит размер кучи на регионы, охватывающие от 1 МБ до 32 МБ, в зависимости от размера кучи. Во время глобальной фазы маркировки определяется живучесть объектов во всей куче. После завершения фазы маркировки G1 знает, какие регионы в основном пусты. Он сначала собирает недостижимые объекты из этих регионов, что обычно дает большое количество свободного места. Таким образом, G1 сначала собирает эти регионы (содержащие мусор), отсюда и название Garbage-First. G1 также использует модель прогнозирования пауз для достижения цели пользовательских времен остановок. Он выбирает количество регионов для сбора на основе заданной цели времени остановок. Цикл сборки мусора G1 включает фазы, показанные на рисунке:
-
Фаза только для молодых объектов: Эта фаза включает только объекты молодого поколения и переводит их в старое поколение. Переход между фазой только для молодых объектов и фазой освобождения пространства начинается, когда старое поколение занимает определенный порог, то есть порог занятости начальной кучи. В это время G1 планирует начальную маркировку коллекции только для молодых объектов вместо обычной молодежной коллекции.
-
Начальная маркировка: Этот тип коллекции запускает процесс маркировки в дополнение к обычной молодежной коллекции. Конкурентная маркировка определяет все текущие живые объекты в регионах старшего поколения, которые будут сохранены для следующей фазы освобождения пространства. Во время маркировки могут происходить обычные молодежные коллекции, пока маркировка не завершится полностью. Маркировка заканчивается двумя специальными паузами, останавливающими мир: Remark и Cleanup.
-
Remark: Эта пауза завершает сам процесс маркировки и выполняет глобальную обработку ссылок и выгрузку классов. Между Remark и Cleanup G1 вычисляет сводку информации о живучести параллельно, которая будет завершена и использована в паузе Cleanup для обновления внутренних структур данных.
-
Очистка: Эта пауза также занимается полностью пустыми регионами и определяет, будет ли фактически следовать фаза восстановления пространства. Если следует фаза восстановления пространства, то фаза только для молодых поколений завершается единственным сбором только для молодых поколений.
-
Фаза восстановления пространства: Эта фаза состоит из нескольких смешанных сборок – помимо регионов молодого поколения, также эвакуирует живые объекты регионов старого поколения. Фаза восстановления пространства заканчивается, когда G1 определяет, что эвакуация дополнительных регионов старого поколения не даст достаточно свободного места, чтобы оправдать усилия.
G1 можно включить, используя флаг -XX:+UseG1GC
. Эта стратегия снижает вероятность исчерпания кучи до того, как фоновые потоки завершат сканирование недостижимых объектов. Кроме того, она компактизирует кучу на лету, что сборщик мусора CMS может делать только в режиме STW. В Java 8 предоставляется прекрасная оптимизация сборщика G1, называемая дедупликацией строк. Как известно, массивы символов, представляющие наши строки, занимают большую часть кучи. Была внесена новая оптимизация, которая позволяет сборщику G1 идентифицировать строки, которые дублируются более одного раза по всей нашей куче, и модифицировать их так, чтобы они указывали на один и тот же внутренний массив char[], чтобы избежать нескольких копий одной и той же строки, присутствующих в куче ненужно. Мы можем использовать аргумент JVM -XX:+UseStringDeduplication
, чтобы включить эту оптимизацию. G1 является сборщиком мусора по умолчанию в JDK 9.
Java 8 PermGen и Metaspace
Как уже упоминалось ранее, пространство Permanent Generation было удалено начиная с Java 8. Таким образом, в JDK 8 HotSpot JVM используется нативная память для представления метаданных класса, которая называется Metaspace. Большинство выделений под метаданные класса производятся из нативной памяти. Также появился новый флаг MaxMetaspaceSize для ограничения объема памяти, используемого для метаданных класса. Если мы не укажем значение для этого флага, то Metaspace изменится во время выполнения в соответствии с требованиями работающего приложения. Сборка мусора Metaspace запускается, когда использование метаданных класса достигает предела MaxMetaspaceSize. Чрезмерная сборка мусора Metaspace может быть симптомом утечки памяти классов, загрузчиков классов или недостаточного размера для нашего приложения. Вот и все, что касается сборки мусора в Java. Надеюсь, вы поняли различия между различными сборщиками мусора, которые у нас есть в Java. Ссылки: Документация Oracle, G1 GC.
Source:
https://www.digitalocean.com/community/tutorials/garbage-collection-in-java