Ответ 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.