Для чего нужен сборщик мусора?
Сборщик мусора (Garbage Collector) должен делать всего две вещи:
- Находить мусор - неиспользуемые объекты. (Объект считается неиспользуемым, если ни одна из сущностей в коде, выполняемом в данный момент, не содержит ссылок на него, либо цепочка ссылок, которая могла бы связать объект с некоторой сущностью приложения, обрывается);
- Освобождать память от мусора.
Существует два подхода к обнаружению мусора:
- Reference counting;
- Tracing
Reference counting (подсчёт ссылок). Суть этого подхода состоит в том, что каждый объект имеет счетчик. Счетчик хранит информацию о том, сколько ссылок указывает на объект. Когда ссылка уничтожается, счетчик уменьшается. Если значение счетчика равно нулю, - объект можно считать мусором. Главным минусом такого подхода является сложность обеспечения точности счетчика. Также при таком подходе сложно выявлять циклические зависимости (когда два объекта указывают друг на друга, но ни один живой объект на них не ссылается), что приводит к утечкам памяти.
Главная идея подхода Tracing (трассировка) состоит в утверждении, что живыми могут считаться только те объекты, до которых мы можем добраться из корневых точек (GC Root) и те объекты, которые доступны с живого объекта. Всё остальное - мусор.
Существует 4 типа корневых точки:
- Локальные переменные и параметры методов;
- Потоки;
- Статические переменные;
- Ссылки из JNI.
Самое простое java приложение будет иметь корневые точки:
- Локальные переменные внутри
main()метода и параметрыmain()метода; - Поток который выполняет
main(); - Статические переменные класса, внутри которого находится
main()метод.
Таким образом, если мы представим все объекты и ссылки между ними как дерево, то нам нужно будет пройти с корневых узлов (точек) по всем рёбрам. При этом узлы, до которых мы сможем добраться - не мусор, все остальные - мусор. При таком подходе циклические зависимости легко выявляются. HotSpot VM использует именно такой подход.
Для очистки памяти от мусора существуют два основных метода:
- Copying collectors
- Mark-and-sweep
При copying collectors подходе память делится на две части «from-space» и «to-space», при этом сам принцип работы такой:
- Объекты создаются в «from-space»;
- Когда «from-space» заполняется, приложение приостанавливается;
- Запускается сборщик мусора. Находятся живые объекты в «from-space» и копируются в «to-space»;
- Когда все объекты скопированы «from-space» полностью очищается;
- «to-space» и «from-space» меняются местами.
Главный плюс такого подхода в том, что объекты плотно забивают память. Минусы подхода:
- Приложение должно быть остановлено на время, необходимое для полного прохождения цикла сборки мусора;
- В худшем случае (когда все объекты живые) «form-space» и «to-space» будут обязаны быть одинакового размера.
Алгоритм работы mark-and-sweep можно описать так:
- Объекты создаются в памяти;
- В момент, когда нужно запустить сборщик мусора приложение приостанавливается;
- Сборщик проходится по дереву объектов, помечая живые объекты;
- Сборщик проходится по всей памяти, находя все не отмеченные куски памяти и сохраняя их в «free list»;
- Когда новые объекты начинают создаваться они создаются в памяти доступной во «free list».
Минусы этого способа:
- Приложение не работает пока происходит сборка мусора;
- Время остановки напрямую зависит от размеров памяти и количества объектов;
- Если не использовать «compacting» память будет использоваться не эффективно.
Сборщики мусора HotSpot VM используют комбинированный подход Generational Garbage Collection, который позволяет использовать разные алгоритмы для разных этапов сборки мусора. Этот подход опирается на том, что:
- большинство создаваемых объектов быстро становятся мусором;
- существует мало связей между объектами, которые были созданы в прошлом и только что созданными объектами.