Алгоритмы сборки мусора java

Избавляемся от мусора в Java

Для работы любого приложения требуется память. Однако память компьютера ограничена. Поэтому важно ее очищать от старых неиспользуемых данных, чтобы освободить место для новых.

Кто занимается этой очисткой? Как и когда очищается память? Как выглядит структура памяти? Давайте разберем с этим подробнее.

Структура памяти Java

Память в Java состоит из следующих областей:

Структура памяти Java

Native Memory — вся доступная системная память.

Heap (куча) — часть native memory, выделенная для кучи. Здесь JVM хранит объекты. Это общее пространство для всех потоков приложения. Размер этой области памяти настраивается с помощью параметра -Xms (минимальный размер) и -Xmx (максимальный размер).

Stack (стек) — используется для хранения локальных переменных и стека вызовов метода. Для каждого потока выделяется свой стек.

Metaspace (метаданные) — в этой памяти хранятся метаданные классов и статические переменные. Это пространство также является общими для всех. Так как metaspace является частью native memory, то его размер зависит от платформы. Верхний предел объема памяти, используемой для metaspace, можно настроить с помощью флага MaxMetaspaceSize.

PermGen (Permanent Generation, постоянное поколение) присутствовало до Java 7. Начиная с Java 8 ему на смену пришла область Metaspace.

CodeCache (кэш кода) — JIT-компилятор компилирует часто исполняемый код, преобразует его в нативный машинный код и кеширует для более быстрого выполнения. Это тоже часть native memory.

Сборка мусора: введение

Что такое «мусор»? Мусором считается объект, который больше не может быть достигнут по ссылке из какого-либо объекта. Поскольку такие объекты больше не используются в приложении, то их можно удалить из памяти.

Например, на диаграмме ниже объект fruit2 может быть удален из памяти, поскольку на него нет ссылок.

Мусор

Что такое сборка мусора? Сборка мусора — это процесс автоматического управления памятью. Освобождение памяти (путем очистки мусора) выполняется автоматически специальным компонентом JVM — сборщиком мусора (Garbage Collector, GC). Нам, как программистам, нет необходимости вмешиваться в процесс сборки мусора.

Источник: Oracle.com

Сборка мусора: процесс

Для сборки мусора используется алгоритм пометок (Mark & Sweep). Этот алгоритм состоит из трех этапов:

Mark & Sweep GC

  1. Mark (маркировка). На первом этапе GC сканирует все объекты и помечает живые (объекты, которые все еще используются). На этом шаге выполнение программы приостанавливается. Поэтому этот шаг также называется «Stop the World» .
  2. Sweep (очистка). На этом шаге освобождается память, занятая объектами, не отмеченными на предыдущем шаге.
  3. Compact (уплотнение). Объекты, пережившие очистку, перемещаются в единый непрерывный блок памяти. Это уменьшает фрагментацию кучи и позволяет проще и быстрее размещать новые объекты.

Поколения объектов

Что такое поколения объектов?

Для оптимизации сборки мусора память кучи дополнительно разделена на четыре области. В эти области объекты помещаются в зависимости от их возраста (как долго они используются в приложении).

Поколения в куче

  1. Young Generation (молодое поколение). Здесь создаются новые объекты. Область young generation разделена на три части раздела: Eden (Эдем), S0 и S1 (Survivor Space — область для выживших).
  2. Old Generation (старое поколение). Здесь хранятся давно живущие объекты.

Что такое Stop the World?

Когда запускается этап mark, работа приложения останавливается. После завершения mark приложение возобновляет свою работу. Любая сборка мусора — это «Stop the World».

Что такое гипотеза о поколениях?

Как уже упоминалось ранее, для оптимизации этапов mark и sweep используются поколения. Гипотеза о поколениях говорит о следующем:

  1. Большинство объектов живут недолго.
  2. Если объект выживает, то он, скорее всего, будет жить вечно.
  3. Этапы mark и sweep занимают меньше времени при большом количестве мусора. То есть маркировка будет происходить быстрее, если анализируемая область небольшая и в ней много мертвых объектов.

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

Сборка мусора поколениями

  1. Новые объекты создаются в области Eden. Области Survivor (S0, S1) на данный момент пустые.
  2. Когда область Eden заполняется, происходит минорная сборка мусора (Minor GC). Minor GC — это процесс, при котором операции mark и sweep выполняются для young generation (молодого поколения).
  3. После Minor GC живые объекты перемещаются в одну из областей Survivor (например, S0). Мертвые объекты полностью удаляются.
  4. По мере работы приложения пространство Eden заполняется новыми объектами. При очередном Minor GC области young generation и S0 очищаются. На этот раз выжившие объекты перемещаются в область S1, и их возраст увеличивается (отметка о том, что они пережили сборку мусора).
  5. При следующем Minor GC процесс повторяется. Однако на этот раз области Survivor меняются местами. Живые объекты перемещаются в S0 и у них увеличивается возраст. Области Eden и S1 очищаются.
  6. Объекты между областями Survivor копируются определенное количество раз (пока не переживут определенное количество Minor GC) или пока там достаточно места. Затем эти объекты копируются в область Old.
  7. Major GC. При Major GC этапы mark и sweep выполняются для Old Generation. Major GC работает медленнее по сравнению с Minor GC, поскольку старое поколение в основном состоит из живых объектов.

Преимущества использования поколений

Minor GC происходит в меньшей части кучи (~ 2/3 от кучи). Этап маркировки эффективен, потому что область небольшая и состоит в основном из мертвых объектов.

Недостатки использования поколений

В каждый момент времени одно из пространств Survivor (S0 или S1) пустое и не используется.

Сборка мусора: флаги

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

Источник

Сборка мусора в Java

В этой статье поговорим о сборке мусора (Garbage Collection) в java.

Память в JVM делится на три части:

  • Young generation (молодое поколение).
  • Old generation (старое поколение).
  • Metaspace (Perm Gen).

Young Generation (молодое поколение)

Как следует из названия, Young Generation — это область памяти для новых, вновь создаваемых объектов.

  • Когда область Young Generation заполняется, то запускается минорная сборка мусора (Minor GC).
  • При Minor GC «мертвые» объекты удаляются из Young Generation.
  • Чем больше «мертвых» объектов в Young Generation, тем быстрее выполняется Minor GC.
  • При Minor GC происходит «остановка мира» (stop the world) — все потоки в приложении останавливаются.

Давайте подробнее разберемся со структурой Young generation.

Young generation разделен на три части: Eden, S0, S1.

  • Все новые объекты размещаются в Eden Space.
  • При заполнении Еden space происходит minor GC: все «живые» объекты перемещаются в одно из пространств уцелевших объектов (survivor space): S0 или S1. Допустим, в нашем случае, все объекты будут перемещены в S0.

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

Давайте посмотрим распределение памяти в Visual GC (плагин для VisualVM).

Как видно, в S0 совсем мало объектов, и как только Еden Space заполняется, все объекты, на которые есть ссылки, перемещаются в S1.

Old generation (старое поколение)

  • В Old generation располагаются долгоживущие объекты.
  • Как правило, эта область больше, чем область для young generation.
  • При заполнении (или достижении заданного порога) происходит Major GC.
  • Обычно Major GC выполняются медленнее и реже, чем minor GC.

Как с помощью этой информации можно оптимизировать использование памяти?

Все зависит от типа приложения.

Если у вас много временных объектов, то будет много Minor GC. В этом случае можно использовать параметр XX:NewRatio=1, чтобы распределить 50% на young generation и 50% на old generation.

По умолчанию NewRatio=2, т.е. young generation составляет 1/3 всей кучи (heap).

При множестве долгоживущих объектов можно сделать область старого поколения больше, увеличив NewRatio.

Зачем используется два survivor space?

Вы наверняка задумались над тем, почему используется два survivor space? Для борьбы с фрагментацией памяти. При каждом копировании объектов из eden в survivor, вы получаете пустой eden и один пустой survivor.

Алгоритмы сборки мусора

В JVM есть несколько сборщиков мусора с разными алгоритмами для молодого и старого поколения. Есть три типа алгоритмов.

Serial collector (последовательный сборщик)

Для сборки мусора используется один поток. Подходит для простых приложений с однопроцессорными машинами.

Parallel collector (параллельный сборщик)

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

Concurrent collector (конкурентный сборщик)

Concurrent collector выполняет сборку мусора вместе с работой потоков вашего приложения. Эффективен для приложений, обрабатывающих средние и большие наборы данных и требующих малого времени отклика.

Для молодого и старого поколений можно использовать разные, но совместимые, алгоритмы GC. Например, нельзя использовать Parallel Scavenge для молодого поколения одновременно с Concurrent Mark Sweep для старого, так как Parallel Scavenge не обеспечивает синхронизацию, которая требуется в CMS.

После Java 8 в сборке мусора произошло много изменений, о которых я расскажу в других статьях.

Приглашаем всех желающих на demo-урок «Переопределение, скрытие, передекларация». На занятии рассмотрим переопределение и скрытие методов в Java, а также передекларация и скрытие переменных; познакомимся с четырьмя правилами, а потом ещё и с пятым. Регистрация доступна по ссылке.

Источник

Читайте также:  Learning to write code in python
Оцените статью