Некоторые (по крайней мере, Mono и .NET) сборщики мусора имеют область кратковременной памяти, которую они часто сканируют, и область вторичной памяти, которую они сканируют реже. Моно называет это детской.
Чтобы выяснить, какие объекты могут быть удалены, они сканируют все объекты, начиная с корней, стека и регистров, и удаляют все объекты, на которые больше нет ссылок.
Мой вопрос: как они предотвращают сканирование всей используемой памяти при каждом сборе? В принципе, единственный способ выяснить, какие объекты больше не используются, - это просмотреть все объекты и все их ссылки. Однако это предотвратит замену памяти операционной системой, даже если она не используется приложением и выглядит как огромный объем работы, которую необходимо выполнить, в том числе для «Коллекции питомников». Не похоже, что они много выигрывают, используя детскую.
Я что-то упускаю или сборщик мусора сканирует каждый объект и каждую ссылку каждый раз, когда выполняет сборку?
источник
Ответы:
Фундаментальные наблюдения, которые позволяют сборке мусора поколений избежать сканирования всех объектов старшего поколения:
Во многих средах GC сборщик мусора может помечать объекты или их части таким образом, что при первой попытке записи в них будет запускаться специальный код для записи того факта, что они были изменены. Объект или его часть, которые были изменены, независимо от их генерации, должны быть отсканированы в следующей коллекции, поскольку они могут содержать ссылки на более новые объекты. С другой стороны, очень часто встречается много старых объектов, которые не изменяются между коллекциями. Тот факт, что сканы нижнего поколения могут игнорировать такие объекты, может позволить таким сканациям завершаться гораздо быстрее, чем они могли бы.
Заметьте, между прочим, что даже если не удается определить, когда объекты изменены, и придется сканировать все на каждом проходе GC, сборка мусора поколений все же может улучшить производительность этапа «развертки» уплотняющего сборщика. В некоторых встроенных средах (особенно в тех, где нет разницы в скорости между последовательным и случайным доступом к памяти или нет вообще) перемещение блоков памяти вокруг относительно дорого по сравнению с ссылками на теги. Следовательно, даже если фазу «отметки» нельзя ускорить с помощью коллектора поколений, ускорение фазы «развертки» может быть целесообразным.
источник
GC, на которые вы ссылаетесь, являются сборщиками мусора поколений . Они спроектированы так, чтобы максимально эффективно использовать наблюдение, известное как «младенческая смертность» или «гипотеза поколений», что означает, что большинство объектов становятся недоступными очень быстро. Они действительно сканируют, начиная с корней, но игнорируют все старые объекты . Следовательно, им не нужно сканировать большинство объектов в памяти, они сканируют только молодые объекты (за счет отсутствия обнаружения недоступных старых объектов, по крайней мере, в этот момент).
«Но это неправильно», я слышу, как вы кричите, «старые объекты могут и действительно относятся к молодым объектам». Вы правы, и есть несколько решений для этого, которые все вращаются вокруг получения знаний, быстро и эффективно, какие старые объекты должны быть проверены, а какие безопасно игнорировать. Они в значительной степени сводятся к записи объектов или небольших (больше, чем объекты, но гораздо меньше, чем целая куча) областей памяти, которые содержат указатели на молодые поколения. Другие описывают это гораздо лучше меня, поэтому я просто дам вам пару ключевых слов: маркировка карточек, запоминающиеся наборы, барьеры для записи. Есть и другие методы (в том числе гибриды), но они охватывают общие подходы, которые я знаю.
источник
Чтобы выяснить, какие объекты питомника еще живы, сборщику нужно только просканировать корневой набор и любые старые объекты, которые были видоизменены с момента последней коллекции , поскольку старый объект, который не был недавно мутирован, не может указывать на молодой объект. , Существуют разные алгоритмы для поддержания этой информации с различными уровнями точности (от точного набора измененных полей до набора страниц, на которых могла произойти мутация), но все они, как правило, включают в себя некоторый барьер записи : код, который выполняется для каждой ссылки мутация, которая обновляет бухгалтерию ГК.
источник
Самое старое и простое поколение сборщиков мусора на самом деле сканировало всю память и должно было остановить всю остальную обработку, пока они это делали. Более поздние алгоритмы улучшали это различными способами - делая копирование / сканирование инкрементным, или выполнялись параллельно. Большинство современных сборщиков мусора разделяют объекты по поколениям и тщательно управляют указателями между поколениями, чтобы новые поколения можно было собирать, не мешая старшим.
Ключевым моментом является то, что сборщики мусора работают в тесном сотрудничестве с компилятором и с остальной частью времени выполнения, чтобы поддерживать иллюзию, что он следит за всей памятью.
источник
В основном ... GC использует "сегменты", чтобы отделить то, что используется, а что нет. Как только он делает проверку, он стирает вещи, которые не используются, и перемещает все остальное во 2-е поколение (которое проверяется реже, чем 1-е поколение), а затем перемещает вещи, которые все еще используются, во 2-е ден в 3-е поколение.
Таким образом, вещи в 3-м поколении обычно являются объектами, которые по какой-то причине остаются открытыми, и GC проверяет их не очень часто.
источник
Алгоритм, обычно используемый этим GC, - Наивная метка-развертка.
Вам также следует учитывать тот факт, что этим управляет не сам C #, а так называемый CLR .
источник