Как происходит обновление GC-обновления после уплотнения

Сборщик мусора .NET собирает объекты (восстанавливает их память), а также выполняет уплотнение памяти (чтобы сохранить фрагментацию памяти до минимума).

Мне интересно, так как приложение может иметь много ссылок на объекты, как GC (или CLR) управляет этими ссылками на объекты, когда адрес объекта изменяется из-за сжатия, выполняемого GC.

Ответ 1

Концепция достаточно проста, сборщик мусора просто обновляет любые ссылки на объекты и перенаправляет их на перемещенный объект.

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

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

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

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

Вы можете увидеть это снова в отладчике при включении отладки неуправляемого кода. Посмотрите на окно "Стек вызовов" и обратите внимание на аннотации [Относительно к управляемому переходному процессу] и [Управляемый к коренному переходу]. Отладчик распознает эти файлы cookie. Важно также, так как он должен знать, может ли окно Locals отображать что-либо значимое. Прогулка стека также отображается в рамках, обратите внимание на классы StackTrace и StackFrame. И это очень важно для песочницы, Code Access Security (CAS) выполняет стеки.

Ответ 2

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

Каждый раз, когда рассматривается ссылка, существует три возможности:

  • Это значение null. В этом случае никаких действий не требуется.

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

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

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

Как только объект был перемещен, прежнее содержание его первых трех слов будет доступно в новом месте и больше не понадобится на старом. Поскольку смещение в объекте всегда будет кратным четырем, а отдельные объекты ограничены 2 ГБ каждый, для хранения всех возможных смещений потребуется лишь часть всех возможных 32-битных значений. При условии, что хотя бы одно слово в заголовке объекта имеет значения не менее 2 ^ 29, оно никогда не может удерживаться ничем иным, чем маркером перемещения объекта, и при условии, что каждому объекту присваивается не менее двенадцати байтов, возможно, для сканирования объекта можно обрабатывать любые глубину дерева, не требуя какого-либо зависимого от глубины хранения вне пространства, занимаемого старыми копиями объектов, содержимое которых больше не требуется.

Ответ 3

Сбор мусора

Каждое приложение имеет набор корней. Корни определяют места хранения, которые относятся к объектам в управляемой куче или к объектам, для которых установлено значение null. Например, все глобальные и статические указатели объектов в приложении считаются частью корней приложения. Кроме того, любые локальные указатели объектов переменных/параметров в стеке потоков считаются частью корней приложения. Наконец, любые регистры процессора, содержащие указатели на объекты в управляемой куче, также считаются частью корней приложения. Список активных корней поддерживается компилятором "точно в срок" (JIT) и временем выполнения общего языка и становится доступным для алгоритма сбора мусора.

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

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

После проверки всех корней граф сборщика мусора содержит набор всех объектов, которые каким-то образом доступны из корней приложения; любые объекты, которые не находятся на графике, недоступны приложению и поэтому считаются мусором. Сборщик мусора теперь перемещается по куче линейно, ища смежные блоки мусорных объектов (теперь это считается свободным пространством). Затем сборщик мусора переносит ненужные объекты в память (используя стандартную функцию memcpy, которую вы знаете в течение многих лет), удаляя все пробелы в куче. Конечно, перемещение объектов в памяти делает недействительными все указатели на объекты. Поэтому сборщик мусора должен изменить корни приложения так, чтобы указатели указывали на новые местоположения объектов. Кроме того, если какой-либо объект содержит указатель на другой объект, сборщик мусора также отвечает за исправление этих указателей.

Фиксированный оператор С#

Фиксированный оператор устанавливает указатель на управляемую переменную и "выдает" эту переменную во время выполнения инструкции. Без фиксированных указателей на подвижные управляемые переменные малопригодны, поскольку сбор мусора может непредсказуемо перемещать переменные. Компилятор С# позволяет вам назначать указатель на управляемую переменную в фиксированном выражении.

Сбор мусора: автоматическое управление памятью в Microsoft.NET Framework

fixed Statement (ссылка на С#)