Вопрос по c#, .net – Как GC обновляет ссылки после сжатия

13

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

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

Ваш Ответ

3   ответа
4
Вывоз мусор

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

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

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

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

C # фиксированное утверждение

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

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

Исправленное заявление (C # Reference)

Не понимаю, почему за это проголосовали ... Alan
Благодарность. Я исправил это после того, как это было понижено. Ruben
На самом деле я понимаю, почему, это не отвечает на вопрос вообще. Хотя это прекрасное объяснение работы GC, в нем ничего не говорится о том, какое решение реализовано для исправления указателей. Ответ Ханса Пассанта действительно служит увеличению энтропии, в то время как ответ Рубена имеет нулевую энтропию, очень холодно! потому что ОП (и гуглеры этого вопроса, как я) уже знают все это. Что мы хотим знать, так это то, что сказал Ганс. Я не понижаю голос, но это было бы заслужено. v.oddou
@ v.oddou: я добавил ответ с механизмом для сборщика мусора, останавливающего мир, для обработки обновлений ссылок. supercat
11

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

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

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

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

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

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

Так что просто нужно просмотреть все ссылки и обновить их, чтобы они указывали на новый адрес объекта. Я думал, что происходит какая-то другая "магия". Благодарность lysergic-acid
well, другая реализация могла бы решить использовать указатели с двойным косвенным указанием и обновлять только один секундный указатель в глобальной таблице перенаправления (управляемой GC) при сжатии. но это будет стоитьотличны иметь дело с производительностью предварительной выборки процессора. таким образом, это было компромиссом, что обход GC и обновление вместо прямых указателей было более быстрым глобальным решением. v.oddou
@ v.oddou: Преимущество использования дескрипторов (функция, включенная в Macintosh 1984 года, кстати) заключается в том, что если дескрипторы - это единственное, что может содержать прямые ссылки на память, и код, использующий дескрипторы, должен использовать подходящие протоколы при создании / получении / закреплении / откреплении / освобождении / уничтожении их, тогда GC нужно только поддерживать список дескрипторов, которые были созданы и не уничтожены, и не нужно ничего знать о том, какие ссылки могут существовать на эти ручки. supercat
5

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

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

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

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

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

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

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

После того, как я написал предыдущую команду, я прочитал ее еще несколько раз и подумал об этом сам, думаю, я наконец-то понял :-). Тем не менее, я все еще думаю, что вы должны улучшить объяснение Arnon Axelrod
@ ArnonAxelrod: это лучше? supercat
Этот ответ, кажется, самый технический (что хорошо), но, к сожалению, он не очень ясен. Например, что такое маркер перемещения и когда он установлен? Какие 3 слова? Когда вы говорите «Каждый раз, когда ссылка рассматривается» - на каком этапе? Что такое «смещение в этом смещении»? так далее Arnon Axelrod

Похожие вопросы