Коротко о Mark-Sweep
Чтобы чистить ненужные объекты GC делает так:
1. Обходит граф всех объектов в хипе (heap) и помечает их
2. Не помеченные объекты никто не использует - их можно удалить.
Пример кода
Рассмотрим такой код:
Так будет выглядеть дерево зависимостей для каждого телефона Nokia.
Коротко о поколениях
Чтобы каждый раз не обходить полностью весь граф объектов, GC разделяет объекты на поколения:
- Young generation (созданы недавно; возможно, скоро можно будет удалить)
- Old generation (созданы давно; скорее всего, проживут еще долго)
Объекты из Old generation хранятся в Old регионе GC.
Объекты из Young generation хранятся в регионах Eden или Survivor.
Что будет происходить с мусором в примере?
Все новые объекты буду создаваться в регионе Eden. Спустя 1 000 000 итераций картина будет выглядеть так:
JVM замечает, что занята значительная доля памяти, и начинает Young GC (GC в молодом поколении)
Young GC
1. Нашли GC roots (безусловно достижимые объекты).
В нашем случае только те, что лежат на стеке. То есть, 1 Nokia, 1 Siemens и 1 iPhone.
2. Итеративно помечаем их и всех, от кого они зависят
3. Помеченных перекладываем в регион Survivor (выжившие). Остальных можно уничтожить (освободить память).
4. Если объект переживает несколько Young GC в Survivor, то он переносится в регион Old, чтобы не тратить на него время при следующих Young GC
Этот алгоритм GC называется Mark-Sweep-Compact (пометили используемые, смели остальные, сжали тех, кто долго выживал)
Идет время.
Прошло еще 10 000 таких Young GC. Регион Old медленно растет.
JVM понимает, что пора с ней что-то делать. И запускает Old GC.
Old GC
Теперь просматриваются не только объекты из Eden и Survivor, но еще и из Old. Такой GC обычно отнимает значительно больше времени и сил у CPU.
И все повторяется заново пока приложение не закончит работу или упадет с OutOfMemoryError.
Классный доклад про G1 c Java One
Про GC есть много крутых материалов. Мне больше всего помог разобраться этот доклад с Java One.
В реальной жизни
Реальные GC работают по похожему принципу, но используют много оптимизаций для улучшения производительности, например:
1. Регионов Eden, Survivor и Old может быть больше, чем по одному
2. Некоторые фазы GC (например, Mark) могут происходить без остановки приложения
3. Фазы, которые требуют остановку приложения, могут выполнятся параллельно в несколько потоков
4. И еще много ужасающе крутых оптимизаций
P.S.
Как всегда, буду рад любым вопросам и уточнениям в комментариях ;)