{"id":14293,"url":"\/distributions\/14293\/click?bit=1&hash=05c87a3ce0b7c4063dd46190317b7d4a16bc23b8ced3bfac605d44f253650a0f","hash":"05c87a3ce0b7c4063dd46190317b7d4a16bc23b8ced3bfac605d44f253650a0f","title":"\u0421\u043e\u0437\u0434\u0430\u0442\u044c \u043d\u043e\u0432\u044b\u0439 \u0441\u0435\u0440\u0432\u0438\u0441 \u043d\u0435 \u043f\u043e\u0442\u0440\u0430\u0442\u0438\u0432 \u043d\u0438 \u043a\u043e\u043f\u0435\u0439\u043a\u0438","buttonText":"","imageUuid":""}

Java GC: Mark-Sweep-Compact и поколения

Коротко о Mark-Sweep

Чтобы чистить ненужные объекты GC делает так:1. Обходит граф всех объектов в хипе (heap) и помечает их2. Не помеченные объекты никто не использует - их можно удалить.

Пример кода

Рассмотрим такой код:

Java-код, который плодит миллионы ненужных телефонов

Так будет выглядеть дерево зависимостей для каждого телефона Nokia.

Дерево зависимостей объектов

Коротко о поколениях

Чтобы каждый раз не обходить полностью весь граф объектов, GC разделяет объекты на поколения:

  • Young generation (созданы недавно; возможно, скоро можно будет удалить)
  • Old generation (созданы давно; скорее всего, проживут еще долго)

Объекты из Old generation хранятся в Old регионе GC.

Объекты из Young generation хранятся в регионах Eden или Survivor.

Что будет происходить с мусором в примере?

Все новые объекты буду создаваться в регионе Eden. Спустя 1 000 000 итераций картина будет выглядеть так:

Заполнили Eden новыми объектами

JVM замечает, что занята значительная доля памяти, и начинает Young GC (GC в молодом поколении)

Young GC

1. Нашли GC roots (безусловно достижимые объекты).

В нашем случае только те, что лежат на стеке. То есть, 1 Nokia, 1 Siemens и 1 iPhone.

Нашли GC roots

2. Итеративно помечаем их и всех, от кого они зависят

Пометили все достижимые объекты в хипе

3. Помеченных перекладываем в регион Survivor (выжившие). Остальных можно уничтожить (освободить память).

Переложили выживших в Survivor

4. Если объект переживает несколько Young GC в Survivor, то он переносится в регион Old, чтобы не тратить на него время при следующих Young GC

Тех, кто долго выживал, переложили в Old

Этот алгоритм GC называется Mark-Sweep-Compact (пометили используемые, смели остальные, сжали тех, кто долго выживал)

Идет время.

Прошло еще 10 000 таких Young GC. Регион Old медленно растет.

Регион Old близок к заполнению

JVM понимает, что пора с ней что-то делать. И запускает Old GC.

Old GC

Теперь просматриваются не только объекты из Eden и Survivor, но еще и из Old. Такой GC обычно отнимает значительно больше времени и сил у CPU.

Mark фаза в Old GC
После Old GC

И все повторяется заново пока приложение не закончит работу или упадет с OutOfMemoryError.

В реальной жизни

Реальные GC работают по похожему принципу, но используют много оптимизаций для улучшения производительности, например:

1. Регионов Eden, Survivor и Old может быть больше, чем по одному2. Некоторые фазы GC (например, Mark) могут происходить без остановки приложения3. Фазы, которые требуют остановку приложения, могут выполнятся параллельно в несколько потоков4. И еще много ужасающе крутых оптимизаций

0
Комментарии
-3 комментариев
Раскрывать всегда