Как .NET CLR различает управляемые и неуправляемые указатели? - PullRequest
10 голосов
/ 23 февраля 2011

Все, в конечном счете, JIT вписано в собственный машинный код, поэтому в конечном итоге у нас есть собственный стек в .NET, который GC должен сканировать на предметные указатели всякий раз, когда выполняет сборку мусора.

Теперь возникает вопрос: как сборщик мусора в .NET определяет, является ли указатель на объект в куче GC фактически управляемым указателем или случайным целым числом, которое случается , чтобы иметь значение, которое соответствует действительному адресу?

Очевидно, что если он не может различить два, то могут быть утечки памяти, поэтому мне интересно, как это работает. Или - осмелюсь сказать, - у .NET есть потенциал для утечки памяти? : O

Ответы [ 8 ]

12 голосов
/ 24 февраля 2011

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

Однако ваша точка зрения хорошо принята.Представьте себе совершенно гипотетический мир, в котором в одном и том же процессе происходит два вида управления памятью .Например, предположим, что у вас есть полностью гипотетическая программа под названием «InterMothra Chro-Nagava-Sploranator», написанная на C ++, которая использует традиционное управление памятью с подсчетом ссылок в стиле COM, где все является просто указателем на память процесса, а объекты освобождаются путем вызоваМетод выпуска правильное количество раз.Предположим, что Sploranator гипотетически имеет язык сценариев JabbaScript, который поддерживает пул объектов, собираемых мусором.

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

Один из способов решения этой проблемы - переписать диспетчер памяти Sploranator, чтобы он выделял свои объекты из управляемого пула GC.

Другой способ - использовать эвристику;GC может выделить поток процессора для сканирования всей памяти в поисках целых чисел, которые являются указателями на его объекты.Это звучит как много, но он может пропустить незафиксированные страницы, страницы в собственной управляемой куче, страницы, которые, как известно, содержат только код, и так далее.ГХ может сделать предположение, что если он думает, что объект может быть мертвым, и он не может найти указатель на этот объект в какой-либо памяти вне его контроля, то этот объект почти наверняка мертв.

Обратная сторонаэтой эвристики, конечно, что это может быть неправильно.У вас может быть целое число, которое случайно совпадает с указателем (хотя это менее вероятно в 64-битной области).Это продлит срок службы объекта.Но кого это волнует?Мы уже в ситуации, когда циклические ссылки могут продлить время жизни объектов.Мы пытаемся сделать эту ситуацию лучше , и эта эвристика делает это.То, что оно не идеально, не имеет значения;это лучше, чем ничего.

Другой способ ошибиться состоит в том, что Sploranator может иметь кодированный указатель, скажем, переворачивая все его биты при сохранении значения и только переворачивая еговернуться прямо перед звонком.Если Sploranator активно враждебен к этой эвристической стратегии GC, то это не сработает.

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

3 голосов
/ 23 февраля 2011

Сборщику мусора не нужно выводить , является ли конкретный шаблон байта (4 или 8 байтов) указателем или нет - он уже знает .

В CLR все строго типизировано, поэтому сборщик мусора знает, являются ли байты int, long, ссылкой на объект, нетипизированным указателем и т. Д. И т. Д.

Расположение объекта в памяти определяется типом компиляции - метаданные, хранящиеся в сборке, дают тип и местоположение каждого члена экземпляра.

Схема стековых фреймов аналогична - JITter выкладывает стековый фрейм при компиляции метода и отслеживает, где и где хранятся данные. (Это делается JITter для обеспечения разных оптимизаций в зависимости от возможностей вашего процессора).

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

Блог Эрика Липперта - хорошее место, чтобы узнать больше - Ссылки не являются адресами было бы местом для начала.

1 голос
/ 23 февраля 2011

Взгляните на Сборка мусора: автоматическое управление памятью в Microsoft .NET Framework
(Некоторые технические детали могут быть немного устаревшими, но описанная структура действительна.)

Несколько кратких замечаний из статьи ...

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

...

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

...

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

На вопрос ...

... или случайное целое число, которое происходит с иметь значение, которое соответствует действительный адрес? .... утечки памяти?

Если объект недостижим, ГК уничтожит его независимо от этого.

1 голос
/ 23 февраля 2011

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

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

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

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

1 голос
/ 23 февраля 2011

Помните, что вся управляемая память управляется CLR.Любая фактическая управляемая ссылка была создана CLR.Он знает, что он создал, а что нет.

Если вы действительно чувствуете, что должны знать детали реализации, то вам следует прочитать CLR через C # Джеффри Рихтер.Ответ не прост - это цитата немного больше, чем можно ответить на SO.

0 голосов
/ 04 августа 2013

Согласно книге "CLR via C #", среда выполнения точно знает, где найдут ссылки / указатели, проверив "внутреннюю таблицу метода".Что эта внутренняя таблица содержит в реализации Microsoft, неизвестно, но она может точно идентифицировать кадры вызовов в стеке, локальные переменные и даже то, какое значение имеют регистры для каждого адреса EIP.

Моно реализация использовалаконсервативное сканирование, что означает, что каждое значение в стеке рассматривается как потенциальный указатель.Это не только приводит к утечкам памяти, но также (поскольку он не может обновить эти значения) объекты, идентифицированные этим, обрабатываются как закрепленные (неподвижные с помощью GC compactor) и это приводит к фрагментации памяти.

Теперь моноимеет опцию «Точная маркировка стека», которая использует GCMaps.Вы можете прочитать больше об этом здесь http://www.mono -project.com / Generational_GC # Precise_Stack_Marking

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

0 голосов
/ 23 февраля 2011

Ссылки имеют заголовки, поэтому это не просто случайное целое число.

0 голосов
/ 23 февраля 2011

Когда вы создаете новый объект ссылочного типа в .NET, вы автоматически «регистрируете» его в CLR и его GC.Невозможно ввести случайные типы значений в этот процесс.Другими словами:

CLR не поддерживает большую неорганизованную кучу указателей, смешанных с типами значений.Он просто отслеживает объекты, созданные в CLR (в любом случае, для целей сбора мусора). Любой тип значения будет кратковременным в стеке или будет членом экземпляра класса.У ГК нет шансов запутаться.

...