Как the.NET CLR различают управляемые и неуправляемые указатели?

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

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

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

8 ответов


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

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

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

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

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

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

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

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


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

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

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

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

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

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


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

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

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

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


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

некоторые краткие моменты из статьи....

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

...

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

...

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

на вопрос...

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

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


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

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


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

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


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

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

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

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


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