трехцветная маркировка
Основная цель сборщика мусора GC — обеспечить рециркуляцию памяти, два основных шага в этом процессе: маркировка памяти и рециркуляция памяти.
Введение в метод трехцветной маркировки
Метод трехцветной маркировки в основном предназначен для эффективной маркировки блоков памяти, которые можно восстановить.
Трехцветная маркировка используется в качестве инструмента для облегчения вывода.Объекты, встречающиеся в процессе обхода графа объектов, помечаются следующими тремя цветами в соответствии с условием «посещены или нет»:
- Белый: указывает, что сборщик мусора не обращался к объекту. Очевидно, что в начале анализа доступности все объекты белые, если в конце анализа объекты остаются белыми, значит, они недоступны.
- Черный: указывает, что к объекту обращался сборщик мусора, и все ссылки на этот объект были просканированы. Черный объект означает, что он был просканирован и его можно сохранить.Если есть другие ссылки на объект, указывающие на черный объект, нет необходимости сканировать его снова. Черный объект не может указывать прямо (без прохождения через серый объект) на белый объект.
- Серый: указывает на то, что сборщик мусора обратился к объекту, но имеется по крайней мере одна ссылка на объект, который не был просканирован.
Процесс трехцветной маркировки
Процесс маркировки:
- В начале параллелизма GC все объекты белые;
- Отметьте все объекты, непосредственно примененные GC Roots, серым набором;
- Если установлено, что объект в сером наборе не имеет дополнительных ссылок, он будет помещен в черный набор.Если есть объекты дополнительных ссылок, все его объекты дополнительных ссылок будут сохранены в сером наборе, и текущий объект будет помещен в серый набор.
- Выполните этот шаг 3 и так далее, пока все объекты в сером наборе не станут черными, этот раунд маркировки завершен, а объекты в белом наборе называются недостижимыми объектами, то есть мусорными объектами.
- После окончания маркировки белые объекты становятся недоступными для GC Roots и могут быть удалены сборщиком мусора.
неправильно помечен
В процессе трехцветной маркировки поток маркировки и пользовательский поток выполняются одновременно, поэтому возможно, что в процессе маркировки пользовательский поток модифицирует ссылочное отношение и неправильно пометит объект, который должен быть переработан, как живой. (Проще говоря, это объект, который был помечен сборщиком мусора черным. Во время параллельного процесса цепочка ссылок пользовательского потока разрывается, в результате чего появляются белые объекты, которые на самом деле должны быть мусором, но все еще остаются черными, то есть плавающим мусором) . Что делать с мусором, генерируемым в это время? Ответ — нет, оставьте это для следующей сборки мусора.
Проблема неправильной маркировки означает, что мусор, который должен быть живым, помечен как мертвый. Это может привести к очень серьезным ошибкам. Так как же появляется этот мусор?
По пути объект A отмечен черным цветом, а два объекта B и C, к которым он относится, находятся в отмеченной серой стадии. В это время пользовательский поток удаляет связь между B->D и устанавливает связь между A->D. В это время объект B еще не просканирован, а объект A уже просканирован и не будет продолжать сканирование вниз. Следовательно, объект D будет удален сборщиком мусора.
Что такое неправильная маркировка? При одновременном выполнении следующих двух условий возникает ошибка:
- Оценщик вставил одну или несколько ссылок на черный объект в белый объект.
- Оценщик удаляет все прямые или косвенные ссылки с серого объекта на белый объект.
Неправильно помеченные решения
Чтобы решить проблему неправильной маркировки, вам нужно всего лишь нарушить любое из этих двух условий.Существует два решения: добавочное обновление (Incremental Update) и моментальный снимок в начале (STAB).
Инкрементное обновление
Первое условие, которое должно быть уничтожено инкрементным обновлением, заключается в том, что когда черный объект вставляется в новое отношение ссылки, указывающее на белый объект, вновь вставленная ссылка будет записана, а после завершения одновременного сканирования записанное отношение ссылки будет Черный объект внутри — это корень, отсканируйте его еще раз. Это можно упростить, поскольку черный объект снова становится серым после вставки новой ссылки на белый объект.
Необработанный снимок (STAB)
То, что исходный снимок хочет уничтожить, является вторым условием.Когда серый объект хочет удалить ссылочное отношение к белому объекту, ссылка, которую нужно удалить, записывается.Серый объект является корнем, и он снова сканируется. Это также можно упростить, чтобы означать, что независимо от того, удалена эталонная связь или нет, поиск будет выполняться по снимку графа объектов в тот момент, когда сканирование только началось.
Отсутствует и множественная маркировка
На самом деле есть две ситуации, в которых неправильная маркировка подразделяется, а именно: отсутствие маркировки и множественная маркировка.
Мультистандартный - плавающий мусор
Если отметка выполняется до тех пор, пока E не выполняется в этот моментobject.E = null
В настоящее время E/F/G теоретически могут быть переработаны. Но так как E сталсерый, то он будет продолжать выполняться. Конечным результатом является то, что они не будут помечены как мусорные объекты и переживут этот раунд маркировки.
Мусор, который должен быть собран в этом раунде, не собирается, эта часть называется «плавающий мусор». Плавающий мусор не влияет на корректность работы программы, этот "мусор" убирается только при запуске очередной сборки мусора.
Кроме того, новые объекты, созданные в процессе маркировки, по умолчанию помечаются черным цветом, но могут стать «мусором» в процессе маркировки. Это тоже часть плавающего мусора.
Отсутствует отметка - барьер чтения и записи
Барьер записи (барьер хранения)
При присвоении значения переменной-члену объекта базовый код выглядит следующим образом:
/**
* @param field 某个对象的成员属性
* @param new_value 新值,如:null
*/
void oop_field_store(oop* field, oop new_value) {
*fieild = new_value // 赋值操作
}
Так называемый барьер записи на самом деле добавляет некоторую логику обработки до и после операции присваивания (аналогично АОП).
void oop_field_store(oop* field, oop new_value) {
pre_write_barrier(field); // 写屏障-写前屏障
*fieild = new_value // 赋值操作
pre_write_barrier(field); // 写屏障-写后屏障
}
Барьер записи + SATB
Когда ссылка на переменную-член объекта E изменяется (objE.fieldG = null;), мы можем использовать барьер записи для изменения Eссылка на исходную переменную-членЗаписи объекта G:
void pre_write_barrier(oop* field) {
oop old_value = *field; // 获取旧值
remark_set.add(old_value); // 记录 原来的引用对象
}
【когдаПеред изменением ссылки на исходную переменную-член запишите исходный ссылочный объект.】
Идея этого подхода заключается в следующем:Попытка сохранить граф объектов в начале, т.е. моментальный снимок в начале (SATB),когдакогда-тоПосле того, как корни GC определены,тогдаОпределен граф объекта.
НапримертогдаD относится к G, и последующие метки также должны следовать за графом объекта в этот момент (D относится к G). Если период изменяется, его можно записать, чтобы убедиться, что разметка по-прежнему соответствует исходному виду.
Стоит отметить, что операция сканирования всех корней GC (т.е. начальная маркировка) обычно требует STW, в противном случае она может никогда не быть просканирована, так как новые корни GC могут быть добавлены во время параллелизма.
SATB уничтожает Условие 1: [Серый объект отключает ссылку на белый объект], тем самым гарантируя, что ни одна метка не будет пропущена.
Небольшая оптимизация: если он не находится на стадии параллельной маркировки сборки мусора или уже был помечен, запись больше не требуется, поэтому можно добавить простое суждение:
void pre_write_barrier(oop* field) {
// 处于GC并发标记阶段 且 该对象没有被标记(访问)过
if($gc_phase == GC_CONCURRENT_MARK && !isMarkd(field)) {
oop old_value = *field; // 获取旧值
remark_set.add(old_value); // 记录 原来的引用对象
}
}
Барьер записи + добавочное обновление
Когда ссылка на переменную-член объекта D изменяется (objD.fieldG = G;), мы можем использовать барьер записи для изменения Dссылка на новую переменную-членЗаписи объекта G:
void post_write_barrier(oop* field, oop new_value) {
if($gc_phase == GC_CONCURRENT_MARK && !isMarkd(field)) {
remark_set.add(new_value); // 记录新引用的对象
}
}
【Когда новая ссылка вставлена, запишите новый объект ссылки】
Идея этого подхода заключается в том, чтобы не требовать сохранения исходного снимка, аДля новых цитат, запишите его и дождитесь обхода, то есть инкрементного обновления (Incremental Update).
Инкрементное обновление разрушает второе условие: [черный объект повторно ссылается на белый объект], таким образом гарантируя, что ни одна метка не будет пропущена.
Грузовой барьер
oop oop_field_load(oop* field) {
pre_load_barrier(field); // 读屏障-读取前操作
return *field;
}
Барьер чтения напрямую нацелен на первый шагvar objF = object.fieldG;
,
void pre_load_barrier(oop* field, oop old_value) {
if($gc_phase == GC_CONCURRENT_MARK && !isMarkd(field)) {
oop old_value = *field;
remark_set.add(old_value); // 记录读取到的对象
}
}
Эта практика консервативна, но и безопасна. Поскольку в Условии 2 [черный объект повторно ссылается на белый объект], предпосылка повторной ссылки состоит в том, что должен быть получен белый объект, и в это время сработает барьер чтения.
Трехцветная нотация и сборщик мусора
Инкрементное обновление: CMS Исходный снимок (STAB):G1, Шенандоа
Справочная документация
- woohoo.brief.com/afraid/12544из 0объявления…
- Позже Лу Мэн-group.ITeye.com/group/topic…
- Woohoo. Oracle.com/веб-папка/he…
- Специальности.Meituan.com/2016/09/23/…
- «Глубокое понимание виртуальной машины JVM — третье издание» Чжоу Чжимин
- blog.CSDN.net/WeChat_4718…