{"id":13893,"url":"\/distributions\/13893\/click?bit=1&hash=172516e20532711a15f10926ee782139b37af1465c4e8ddd35ef0e9b5c0244fd","title":"\u041a\u0430\u043a \u044d\u0444\u0444\u0435\u043a\u0442\u0438\u0432\u043d\u043e \u043e\u0431\u0449\u0430\u0442\u044c\u0441\u044f \u0441 \u043a\u043b\u0438\u0435\u043d\u0442\u0430\u043c\u0438?","buttonText":"","imageUuid":"","isPaidAndBannersEnabled":false}

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
Комментарии
Читать все 0 комментариев
null