Являются ли эти объекты ссылками на стек или на кучу?

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

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;
}

Ответ 1

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

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, работающем на С# 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 версии 4.0. Если вы хотите знать, как материал хранится в стеке, кучи и регистры в некоторой другой конфигурации, это может быть совершенно другим. Все записи могут храниться в куче или все в реестрах; вообще не может быть стека. Это полностью зависит от того, как авторы jit-компилятора решили реализовать семантику IL.

Ответ 2

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

Ответ 3

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

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

Ответ 4

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

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

Ответ 5

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)
}

Ответ 6

Прочтите Jeff Richter CLR через С# для полного понимания этой темы.

Ответ 7

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

Ответ 8

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