Java Garbage Collection

В предыдущей статье мы обсуждали метод finalize() у класса Object, который вызывался при сборке мусора и рассмотрели 2 случая, в ходе которых было не так понятно как работает Garbage Collection в Java. Поэтому давайте прочитаем оффициальную документацию от Оракла.

Garbage Collection basics

Итак, автоматическая сборка мусора в Java это процесс выявляения в памяти более неиспользуемых объектов (ссылок на них) и удаление. Это если говорить просто и в 2 словах. Теперь, давайте чуть детальней рассмотрим структуру памяти в JVM.

Когда мы запускаем наше приложение, то в первую очередь из Java классов формируются файлы с тем же именем, но с расширением .class, в которых находится обфусцированный джавабайткод. Происходит это потому, что с чистым джава кодом работает программист, а для машины он непонятен и приходится компилировать в понятный машине язык (Just-In-Time compiler). Во время исполнения (runtime) примитивы кладутся в память, называемую Thread stack, а объекты кладутся в Heap память. Именно с ней и работает сборщик мусора, что явно видно на рисунке.

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

Весь этот процесс происходит в несколько шагов. Рассмотрим детальней.

Шаг 1. Сначала сборщик мусора должен пометить объекты как неиспользуемые.

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

Шаг 2. Удаление неиспользуемых объектов и ссылок на них.

На картинке наблюдаем обычное удаление неиспользуемых объектов, по окончанию этого процесса в памяти остаются те объекты, ссылки на которых используются в программе. Как видим между ними остаются пустоты. Поэтому после обычного удаления идет процесс «утромбования».

Но как мы все понимаем, если каждый раз вызывать этот процесс и помечать объекты как неиспользуемые, после этого удалять и перемещать в начало, то сам процесс работы сборщика мусора довольно таки затратен. Поэтому, на самом деле, не все так просто как  показано на картинках. В действительности Heap память (куча) делится на несколько сегментов : Young Generation, Old Generation & Permanent Generation.

Young Generation это тот участок памяти, куда попадают новые объекты. Когда он заполняется, то происходит минорный сбор мусора. После которого объекты, которые не были собраны перемещаются в Old Generation периодически. Событие сбора мусора на минорной стадии называется Остановка Вселенной (Stop the World Event), потому что в это время все в приложении останавливается и ждет пока произойдет сбор мусора.

В Old Generation лежат все объекты, которые долго живут в приложении. Когда происходит сбор мусора в Old Generation это событие называют мажорный сбор мусора. Также как и минорный сбор мусора, мажорный также называется Остановка Вселенной. Поэтому важно для оптимизации приложения как можно реже вызывать сбор мусора.

Permanent Generation содержит метаданные, которые требует JVM для описания классов и методов, которые используются в приложении. Также в этой области хранятся классы и методы библиотеки Java SE.

А теперь еще подробнее посмотрим на каждый процесс аллокации и делокации в Young Generation.

Сначала объекты попадают в Eden, области Survivor s0, s1 пусты. После того как Eden заполняется, происходит процесс помечания объектов как «живых/мертвых» т.е. используемых и более неиспользуемых (есть ссылка на этот объект или более нет).

После процесса помечания все живые объекты переходят в область Survivor s0, а мертвые объекты удаляются в процессе minor garbage collection.

При следующей минорной сборке мусора происходит то же самое, но на этот раз объекты переходят также в область Survivor s1, т.е. те, которые были в s0 и не были удалены сборщиком устаревают (инкремент возраста) и переходят в s1. (цифры 1 и 2 показывают возраст объектов, т.е. сколько процессов сбора мусора они пережили)

При последующей сборке мусора происходит то же самое, но! На этот раз происходит переключение памяти survivor. Т.е. те объекты, которые лежали в s1 переходят в s0, а Eden и s1 очищаются.

После некоторого возраста объекты перемещаются в Old Generation. На рисунке это возраст после 8 (т.е. 8 раз произошел сбор мусора и эти объекты не были убиты).

Этот процесс называется Promotion (на рисунке написано Tenured, что и есть Old Generation)

Подведем итог. Новые объекты создаются и кладутся в Eden, после сбора мусора, когда память в Eden заполняется, те объекты, на которые есть ссылки кладутся в память Survivor, сначала в s0, при следующей очистке с увеличением возраста идут в s1 после чего при последующем сборе увеличивается возраст и Eden вместе с s1 очищаются, а все объекты, которые остались живыми кладутся в s0. После некоторого повторения все живые объекты переходят в Old Generation. Когда забивается Old Generation, происходит мажорный сбор мусора и (как это ни странно) если объектов слишком много и место не освобождается, то мы имеем ошибку OutOfMemory. Поэтому важно следить за использованием памяти и не хранить объекты и ссылки на них, если в них нет необходимости.

p.s Объемы памяти могут быть регулируемы и в некоторых случаях это весьма улучшит работоспособность приложения. Но рекомендуется с умом использовать ссылки, чтобы не приводить к состоянию OutOfMemory и/или Leak Memory (когда некоторые объекты, которые вы не используете не удаляются сборщиком мусора и долго живут в приложении).

Запись опубликована в рубрике Программирование Java. Добавьте в закладки постоянную ссылку.

Добавить комментарий

Ваш e-mail не будет опубликован. Обязательные поля помечены *