Одно время я думал, что для этих вещей достаточно самостоятельного чтения книг, они относились к тому роду вещей, в которых нужно полдня окончательно разобраться, а потом, естественно, забыть через два дня.
В итоге, что это такое, что такое карточный стол, что такое метод трехцветной маркировки, все эти вопросы-призраки задаются на собеседованиях, и работа закончена.
Подсчет ссылок и анализ достижимости
Чтобы выполнить сборку мусора, мы должны сначала решить, как определить, жив ли объект? Вообще есть два пути.
подсчет ссылок, Добавить счетчик к объекту. Всякий раз, когда есть ссылка на него, счетчик будет +1, в противном случае он будет -1, когда ссылка недействительна. Тогда объект со значением счетчика 0 является объектом, который может быть переработаны, но есть проблема, которую нельзя решить циклической ссылкой.
Для текущей виртуальной машины используется основной алгоритм:Алгоритмы анализа достижимости.
Сначала определите набор корневых объектов GC ROOTS, выполните поиск по GC ROOTS, и путь, пройденный процессом поиска, называетсяцепочка ссылок, если у объекта нет ни одной цепочки ссылок на GC ROOTS, то объект недоступен и может быть переработан.
Недостижимый объект необходимо пометить дважды, при первом обнаружении отсутствия связанной цепочки ссылок он будет помечен в первый раз, если необходимо выполнить метод finalize(), то объект будет помещен в очередь для ожидания выполнения finalize().Когда finalize() успешно связан с другими объектами в цепочке ссылок, он будет удален из коллекции повторно используемых объектов. (но метод finalize() не рекомендуется)
Коллекция поколений
Исходя из того, как судить о выживании объектов, следующий вопрос заключается в том, как выполнять сборку мусора GC.В настоящее время коммерческие виртуальные машины в основном представляют собой реализацию сбора поколений.Его реализация основана на двух предположениях:
- Подавляющее большинство объектов смертны
- Объекты, пережившие большее количество сборок мусора, труднее умереть
На основе этих двух гипотез производятся молодое поколение и старое поколение, которые мы обычно видим сегодня.
Из-за поколения, поэтому GC тоже является поколением.
Молодое поколение используется для хранения этих мертвых быстрых объектов.Сборщик мусора молодого поколения называется MinorGC.Каждый раз, когда памяти молодого поколения недостаточно, мы запускаем MinorGC, и если в будущем все еще будут выжившие объекты, мы решите, основываясь на количестве опыта MinorGC и динамическом суждении о возрасте.
Старое поколение хранит бессмертные объекты.Здесь GC называется OldGC.Сейчас многие называют его FullGC.На самом деле это не точно.FullGC вообще должен относиться к GC молодого поколения и старого поколения.
В соответствии с алгоритмом анализа достижимости, который мы упоминали выше, чтобы судить о выживании объектов, если мы выполним MinorGC, будут ли объекты, на которые ссылается старость? Будет ли больше объектов, на которые ссылается молодое поколение после выполнения OldGC?
Если это так, то когда мы выполняем MinorGC, мы должны не только заботиться о корнях GC, но и преодолевать старость.Эта проблема с производительностью очень большая.
Итак, вот еще одна гипотеза. . .
Цитат из разных поколений очень мало по сравнению с современными цитатами..
Из этого создается новое решение. Нам не нужно сканировать все старое поколение. Нам нужно только построить структуру данных в молодом поколении, называемую запоминаемым набором, которая делит старое поколение на N областей и отмечает, какие регион будет Есть межпоколенческие ссылки.
При выполнении MinorGC в будущем просто добавьте эти области памяти, содержащие межпоколенческие ссылки на GC Roots, для совместного сканирования.
карточный стол
Сказав это, я пришел к первой теме:карточный стол.
Карточная таблица на самом деле является реализацией набора памяти.Если набор памяти является интерфейсом, то карточная таблица является его классом реализации.
Для виртуальной машины HotSpot карточная таблица реализована в виде байтового массива.
CARD_TABLE [this address >> 9] = 0;
Этот код представляет собой логику разметки карточного стола. На самом деле таблица карт предназначена для отображения адреса памяти блока, эти блоки адресов памяти называютсястраница карты, из кода видно, что размер каждой страницы карточки 2^9=512 байт.
При преобразовании в шестнадцатеричный формат элементы 0 и 1 массива сопоставляются со страницей карты памяти с адресом 0x0000~0x01FF (0-511) и 0x0200~0x03FF (512-1023).
Пока объект на странице карты имеет один или несколько указателей объектов перекрестного создания, элемент массива таблицы карт позиции изменяется на 1, указывая на то, что позиция грязная, и на 0, если нет.
Во время GC просто добавьте указатель объекта страницы карты со значением 1 в корни GC для совместного сканирования.
С карточным столом нам не нужно сканировать всю старость, когда происходит MinorGC, и производительность значительно повышается.
проблема с карточным столом
написать барьер
Элемент массива таблицы карт нужно изменить на 1, то есть на состояние dirty.Для HotSpot это реализовано через барьер записи.По сути, когда другие поколения ссылаются на объект текущего поколения, присваивается ссылка. Время обновлять, метод обновления аналогичен аспекту АОП.
void oop_field_store(oop* field, oop new_value) {
// 引用字段赋值操作
*field = new_value;
// 写后屏障,在这里完成卡表状态更新
post_write_barrier(field, new_value);
}
Проблема с барьерами записи заключается в дополнительной нагрузке на производительность, но это не большая проблема и вполне приемлема.
ложный обмен
Другая проблема — это то, о чем я писал в своей предыдущей статье, проблема ложного обмена (если вы не знаете, что такое ложный обмен, пожалуйста, прочитайте мою предыдущую статью).
Строка кэша обычно составляет 64 байта, один элемент таблицы карточек — 1 байт, а размер занимаемой страничной памяти карточки — 64*512=32 КБ.
Если несколько потоков будут обновлять объекты, которые попадают в этот диапазон 32 КБ, это приведет к снижению производительности.
Как решить проблему ложного обмена?
Новый параметр был добавлен после JDK7-XX:+UseCondCardMark, который представляет решение о том, следует ли открывать обновление таблицы карт, и помечается как грязное, если оно не было помечено.
if (CARD_TABLE [this address >> 9] != 0)
CARD_TABLE [this address >> 9] = 0;
трехцветная маркировка
Карточные таблицы решают проблемы производительности с помощью сбора данных между поколениями и перечисления корневых узлов. Благодаря этим мерам пауза STW, вызванная процессом перечисления корневого узла, уже находится в контролируемом диапазоне.
Кроме того, есть еще одна проблема, которая заключается в том, чтобы начать обход от корней GC, как эффективно маркировать эти объекты, что является ролью метода трехцветной маркировки. Потому что если в куче больше объектов, то явно разметка будет генерировать более длинные паузы.
Взяв в качестве примера известную CMS или G1, первые два шага GC выглядят следующим образом:
- Начальная отметка: отметьте объекты, с которыми может быть связан GC ROOT.Для этого шага требуется STW, но время паузы очень короткое.
- Параллельная маркировка: Процесс обхода всего графа объектов от непосредственно ассоциированного объекта GCRoots займет много времени, но теперь его можно выполнять параллельно с пользовательскими потоками.Проблема эффективности — забота о трехцветной маркировке.
В методе трехцветной маркировки объекты, пройденные из корней GC, маркируются следующими тремя цветами:
-
Белый, в начале обхода все объекты белые
-
Серый, просканирован сборщиком мусора, но хотя бы одна ссылка осталась непросканированной
-
Черный, он был просканирован сборщиком мусора, и все ссылки на этот объект также были просканированы, и это безопасный и живой объект
Весь процесс разметки выглядит следующим образом: во-первых, при обходе от GC Roots все объекты должны быть белыми.
Затем объект A\G сканируется до тех пор, пока он не станет серым, а затем также сканируются ссылки объекта A\G, и объект A\G становится черным.
Объекты B\C начинают сканироваться и становятся серыми, а их ссылки также становятся черными при сканировании.
Тогда объект D также пройдет процесс от серого до черного (поленитесь и опустите неактуальную карту процесса)
Последние оставшиеся узлы E\F — это объекты, которые можно перерабатывать.
Проблема с трехцветным маркером
Хотя метод трехцветной маркировки очень эффективен, он также может привести к другим проблемам.
Во-первых, выше мы упоминали, что процесс параллельной маркировки не будет STW, то есть ваша мать убирает, а вы продолжаете бросать мусор рядом с собой.
При трехцветной маркировке объект, который необходимо очистить, помечается как живой, чтобы сборщик мусора не мог в этот раз очистить объект, что называется плавающим мусором, и решение состоит в том, чтобы дождаться, пока следующий сборщик мусора его очистит. Просто подожди, пока твоя мама уберет в следующий раз.
И наоборот, пометить живые объекты как нуждающиеся в очистке довольно хлопотно, и ваша программа вот-вот завершится ошибкой.
Поэтому исследования показали, что эта проблема исчезновения объекта возникает только при одновременном выполнении двух условий:
- Вставлена одна или несколько ссылок на черно-белые объекты.
- Удалены все ссылки на серо-белые объекты.
Ну, есть два решения этой проблемы:Инкрементное обновлениеиисходный снимок, если он соответствует сборщику мусора, CMS использует добавочные обновления, тогда как G1 использует необработанные снимки.
Идея состоит в том, что, поскольку оно должно быть выполнено одновременно, то мне нужно разрушить только одно из условий, не так ли?
Итак, давайте посмотрим на сцену в нашем примере выше. Предположим, что A сканируется, а C просто становится серым. В это время ссылка C->D удаляется и добавляется A->D. ), так что D должен стать черным последовательно (черные объекты не должны быть очищены), но так как C->D больше не ссылается, A стал черным объектом, и он не будет пересканирован, так что даже если ссылка на A-> D добавляется, D может стать только белым объектом и в конечном итоге безжалостно очищается.
Решение для инкрементного обновления заключается в том, что он будет записывать эти вновь вставленные ссылки, а после завершения сканирования повторно сканировать с черным объектом в качестве корня. Разве это не похоже на добавочное обновление? Просканируйте только что вставленную запись еще раз!
Исходный снимок предназначен для уничтожения второго условия.Он записывает ссылку, которая будет удалена.После завершения сканирования серый объект используется в качестве корня для повторного сканирования. Так что это как снимок, независимо от того, удалите вы его или нет, по сути, он будет перезапущен в соответствии с предыдущими отношениями в конце.