как ссылки на объекты C# представлены в памяти / во время выполнения (в среде CLR)?

Мне любопытно узнать, как ссылки на объекты C# представлены в памяти во время выполнения (в .NET CLR). Некоторые вопросы, которые приходят на ум:

  1. сколько памяти занимает ссылка на объект? Отличается ли это, когда определено в области класса против области метода? Отличается ли он от этой области (стек против кучи)?

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

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

2 ответов


этот ответ легче всего понять, если вы понимаете указатели C / C++. Указатель-это просто адрес памяти некоторых данных.

  1. ссылка на объект должна быть размером указателя, который обычно составляет 4 байта на 32-разрядном процессоре и 8 байтов на 64-разрядном процессоре. Она одинакова независимо от того, где она определена. Где он живет, зависит от того, где он определен. Если это поле класса, оно будет находиться в куче в объекте, частью которого оно является. Если это статическое поле, оно находится в специальном разделе кучи, который не подлежит сбору мусора. Если это локальная переменная, она живет в стеке.

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

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

одна вещь, которую вы должны знать, типы значений хранятся на месте (нет адреса памяти, вместо этого они хранятся непосредственно там, где адрес памяти будет - см. #1). Когда они передаются методу, создается копия, и эта копия используется в методе. Когда они передаются по ссылке, передается адрес памяти, который находит тип значения в памяти, позволяя его изменять.

Edit: как указал dlev, эти ответы не являются жесткое и быстрое правило, так как нет правила, которое говорит, что так должно быть. .NET свободен для реализации этих вопросов, однако он хочет. Это наиболее вероятный способ реализовать его, Хотя, как это, как процессор Intel работает внутри, поэтому использование любого другого метода, вероятно, будет неэффективным.

надеюсь, я не слишком смутил вас, но не стесняйтесь спрашивать, если вам нужно разъяснение.


.NET кучи и стеки Это тщательное рассмотрение того, как работают стек и куча.

C# и многие другие кучи-использование языков ООП в общем справочном-говорят использование ручки не указатели для ссылок в этом контексте (C# также способен использовать указатели!) Аналогии указателей работают для некоторых общих понятий, но эта концептуальная модель ломается для таких вопросов. Смотрите отличный пост Эрика Липперта по этой теме ручки не Адреса

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

в этом случае CLR использует реальные адреса для дескрипторов: из приведенной выше ссылки:

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

Итак, да, дескриптор, вероятно, 4 байта на 32-битной архитектуре и 8 байтов на 64-байтовой архитектуре, но это не "наверняка", и это не непосредственно из-за указателей. Стоит отметить, в зависимости от реализации компилятора и диапазоны адресов, используемые некоторыми типами указателей, могут быть разными по размеру.

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

последний привод этой тонкой точки:

это указатель на C#:

int* myVariable;

это дескриптор C#:

object myVariable;

они не то же самое.

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