Статьи

Что такое сборка мусора?

Ниже приведен пример из нашего сборника мусора, который будет опубликован в ближайшие недели. Тем временем не торопитесь, чтобы ознакомиться с основами Сборки мусора – это будет самая первая глава книги.

На первый взгляд, сборщик мусора должен иметь дело с тем, что предлагает название – найти и выбросить мусор. В действительности это происходит с точностью до наоборот. Сборщик мусора отслеживает все объекты, которые все еще используются, и помечает остальное как мусор. Имея это в виду, мы начинаем углубляться в подробности того, как процесс автоматического восстановления памяти, называемый «Сборка мусора», реализован для виртуальной машины Java.

Ручное управление памятью

Прежде чем мы начнем охватывать сборку мусора в ее современном виде, давайте кратко расскажем о днях, когда вам приходилось вручную и явно выделять и освобождать память для ваших данных. И если вы когда-нибудь забудете освободить его, вы не сможете повторно использовать память. Память будет востребована, но не использована. Такой сценарий называется утечкой памяти .

Вот простой пример, написанный на C с использованием ручного управления памятью:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
int send_request() {
    size_t n = read_size();
    int *elements = malloc(n * sizeof(int));
 
    if(read_elements(n, elements) < n) {
        // elements not freed!
        return -1;
    }
 
    // …
 
    free(elements)
    return 0;
}

Как мы видим, довольно легко забыть освободить память. Утечки памяти раньше были гораздо более распространенной проблемой, чем сейчас. Вы могли бы действительно бороться с ними, исправляя свой код. Таким образом, гораздо лучшим подходом было бы автоматизировать восстановление неиспользуемой памяти, полностью исключая возможность человеческой ошибки. Такая автоматизация называется сборкой мусора (или GC для краткости).

Умные Указатели

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

01
02
03
04
05
06
07
08
09
10
11
int send_request() {
    size_t n = read_size();
    stared_ptr<vector<int>> elements
              = make_shared(new vector<int>());
 
    if(read_elements(n, elements) < n) {
        return -1;
    }
 
    return 0;
}

Используемый нами shared_ptr отслеживает количество ссылок на него. Это число увеличивается при его передаче и уменьшается при выходе из области видимости. Как только число ссылок достигнет нуля, shared_ptr автоматически удалит базовый вектор.

Автоматизированное управление памятью

В приведенном выше коде C ++ нам все еще приходилось явно указывать, когда мы хотим позаботиться об управлении памятью. Но что, если бы мы могли заставить все объекты вести себя таким образом? Это было бы очень удобно, так как разработчику больше не нужно думать о уборке после себя. Среда выполнения автоматически поймет, что часть памяти больше не используется, и освободит ее. Другими словами, он автоматически собирает мусор . Первый сборщик мусора был там в 1959 году для Лисп, и с тех пор технология только развивалась.

Подсчет ссылок

Идея, которую мы продемонстрировали с общими указателями C ++, может быть применена ко всем объектам. Многие языки, такие как Perl, Python или PHP, используют этот подход. Это лучше всего иллюстрируется рисунком:

Java-GC-счетный-references1

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

Синие круги – это объекты в памяти, вы можете увидеть количество ссылок на них. Наконец, серые круги – это объекты, на которые нет ссылок ни в одной из областей. Таким образом, серые объекты являются мусором и могут быть очищены сборщиком мусора.

Это все выглядит действительно хорошо, не так ли? Ну, это так, но у всего метода есть огромный недостаток. Это довольно легко закончить с отдельным циклом объектов, ни один из которых не находится в области видимости, но из-за циклических ссылок количество их ссылок не равно нулю. Вот иллюстрация:

Java-GC-разметки и развертки

Увидеть? Красные объекты на самом деле являются мусором, который приложение не использует. Но из-за ограничений подсчета ссылок все еще существует утечка памяти.

Есть несколько способов преодолеть это, например, использование специальных «слабых» ссылок или применение отдельного алгоритма для сбора циклов. Упомянутые языки, Perl, Python и PHP, все так или иначе обрабатывают циклы, но это выходит за рамки данного руководства. Вместо этого мы начнем исследовать подход, принятый JVM, более подробно.

Марк и развертка

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

  • Локальные переменные
  • Активные темы
  • Статические поля
  • JNI ссылки
  • Другие (будут обсуждаться позже)

Метод, используемый JVM для отслеживания всех достижимых (живых) объектов и обеспечения возможности повторного использования памяти, используемой недоступными объектами, называется алгоритмом Mark and Sweep. Он состоит из двух этапов:

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

Различные алгоритмы GC в JVM, такие как Parallel Scavenge, Parallel Mark + Copy или CMS, реализуют эти фазы немного по-разному, но на концептуальном уровне процесс остается аналогичным двум шагам, описанным выше.

Принципиально важным в этом подходе является то, что циклы больше не просочились:

Java-GC-счетный-references1

Не очень хорошая вещь состоит в том, что потоки приложения должны быть остановлены для того, чтобы произошел сбор, так как вы не можете реально считать ссылки, если они постоянно меняются. Такая ситуация, когда приложение временно останавливается, чтобы JVM могла заниматься служебной деятельностью, называется паузой Stop The World . Они могут происходить по многим причинам, но сборка мусора на сегодняшний день является самой популярной.

Если вам удалось продвинуться дальше с этой публикацией, я могу только рекомендовать подписаться на нашу ленту Twitter, где мы продолжаем публиковать сообщения по различным темам, связанным с производительностью Java.

Ссылка: Что такое сборка мусора? от нашего партнера JCG Глеба Смирнова в блоге Plumbr Blog .