Ссылки этих объектов в стеке или в куче? - PullRequest
13 голосов
/ 01 апреля 2010

Буду очень признателен, если кто-нибудь скажет, хорошо ли я понимаю:

class X
{
   A a1=new A(); // reference on the stack, object value on the heap
   a1.VarA=5;    // on the stack - value type
   A a2=a1;      // reference on the stack, object value on the heap
   a2.VarA=10;   // on the stack - value type         
}

Кроме того, ссылки a1 и a2 находятся в стеке, а их значения "объекта" - в куче. Но как насчет VarA переменной, все еще чистого типа значения?

class A
{
   int VarA;
}

Ответы [ 8 ]

27 голосов
/ 01 апреля 2010

Вы задаете вопросы о деталях реализации , поэтому ответ будет зависеть от конкретной реализации. Давайте рассмотрим версию вашей программы, которая на самом деле компилируется:

class A { public int VarA; }
class X
{
    static void Main(string[] args)
    {
        A a1 = new A();
        a1.VarA = 5;
        A a2 = a1;
        a2.VarA = 10;
    }
}

вот что происходит на Microsoft CLR 4.0 с C # 4.0 в режиме отладки.

На данный момент указатель кадра стека скопирован в регистр ebp:

Здесь мы выделяем кучу памяти для нового объекта.

A a1 = new A();
mov         ecx,382518h 
call        FFE6FD30 

Возвращает ссылку на объект кучи в eax. Мы храним ссылку в слоте стека ebp-48, который является временным слотом, не связанным ни с одним именем. Помните, a1 еще не был инициализирован.

mov         dword ptr [ebp-48h],eax 

Теперь мы берем ту ссылку, которую мы только что сохранили в стеке, и копируем ее в ecx, которая будет использоваться для указателя «this» на вызов ctor.

mov         ecx,dword ptr [ebp-48h] 

Теперь мы называем ctor.

call        FFE8A518 

Теперь мы снова копируем ссылку, хранящуюся в слоте временного стека, в регистр eax.

mov         eax,dword ptr [ebp-48h] 

И теперь мы копируем ссылку в eax в слот стека ebp-40, который равен a1.

mov         dword ptr [ebp-40h],eax 

Теперь мы должны извлечь a1 в eax:

a1.VarA = 5;
mov         eax,dword ptr [ebp-40h] 

Помните, eax теперь является адресом выделенных в куче данных для объекта, на который ссылается a1. Поле VarA этой вещи составляет четыре байта в объекте, поэтому мы сохраняем 5 в это:

mov         dword ptr [eax+4],5 

Теперь мы делаем копию ссылки в слоте стека для a1 в eax, а затем копируем ее в слот стека для a2, то есть ebp-44.

A a2 = a1;
mov         eax,dword ptr [ebp-40h] 
mov         dword ptr [ebp-44h],eax 

И теперь, как и следовало ожидать, мы снова получаем a2 в eax, а затем добавляем четыре байта ссылки для записи 0x0A в VarA:

a2.VarA = 10;
mov         eax,dword ptr [ebp-44h] 
mov         dword ptr [eax+4],0Ah

Таким образом, ответ на ваш вопрос заключается в том, что ссылки на объект хранятся в стеке в трех местах: ebp-44, ebp-48 и ebp-40. Они хранятся в регистрах в eax и ecx. Память объекта, включая его поле, хранится в управляемой куче. Это все на x86 в отладочной сборке Microsoft CLR v4.0. Если вы хотите узнать, как содержимое хранится в стеке, куче и записывается в какой-то другой конфигурации, это может быть совершенно иначе. Все ссылки могут храниться в куче или все в регистрах; может не быть стека вообще. Это полностью зависит от того, как авторы jit-компилятора решили реализовать семантику IL.

10 голосов
/ 01 апреля 2010

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

2 голосов
/ 01 апреля 2010
class X 
{ 
    A a1=new A(); // reference on the stack, object value on the heap 
    a1.VarA=5;    // on the Heap- value type (Since it is inside a reference type)
    A a2=a1;      // reference on the stack, object value on the heap 
    a2.VarA=10;   // on the Heap - value type (Since it is inside a reference type)
}
2 голосов
/ 01 апреля 2010

В этом случае a1.VarA будет в куче, так как место для него было бы выделено, когда вы сделали A a1 = new A().

Если вы просто выполните int i = 5; в функции, которая будет идти в стеке, но, как вы явно заявили, a1 должен был быть выделен в куче, то все типы значений, связанные с ним, будут помещены в кучу

2 голосов
/ 01 апреля 2010

Я думаю, у вас может быть небольшое недоразумение ...

Вообще говоря, ссылочные типы помещаются в кучу, а типы значений / локальные объекты, которые, я считаю (могут быть неверными), помещаются в стек. Однако ваши примеры A1.VarA и A2.VarA ссылаются на поле ссылочного типа, которое хранится вместе с объектом в куче ...

0 голосов
/ 07 апреля 2010

Я новичок в C # также. Ваш вопрос очень важен, я тоже об этом думал. Вся документация говорит, что значения идут в стек, а ссылки - в кучу, но, как сказали парни выше, это просто для кода внутри методов. На ступеньках обучения я понимаю, что код всех программ начинается внутри метода, который принадлежит экземпляру, который принадлежит куче. Столь концептуально, стек не равен по размеру куче, как вся документация смущает людей. Механизм стека обнаруживается только в методе ...

0 голосов
/ 01 апреля 2010

Помните, что чтение в C # в глубине: - Только локальные переменные (объявленные внутри метода) и параметр метода живут в стеке. Переменная экземпляра, такая как varA в приведенном выше случае, находится в куче.

0 голосов
/ 01 апреля 2010

Прочитайте CLR Джеффа Рихтера через C # для полного понимания этой темы.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...