views:

485

answers:

9

Hi, I would really appreciate if someone could tell me whether I understand it well:

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     

}

Also both a1 and a2 references are on the stack, while their "object" values are on the heap. But what about VarA variable, its still pure value type?

class A
{
int VarA;
}
+8  A: 

Strictly speaking, it is implementation-dependant. Usually, a .NET developer should not care about this things. As far as i know, in Microsoft's implementation of .NET, variables of value types are stored on the stack (when they are declared within a method), and data of reference-type objects is allocated on a managed heap. But, remember, when a value type is a field of a class, the class data itself is stored on a heap (including all value-type fields). Hence, don't mix semantics(value types vs reference types) with allocation rules. This things may or may not be correlated.

n535
+2  A: 

I think you might have have a slight misunderstanding...

Generally speaking, reference types go on the heap, and value types / locals I believe (may be wrong) go on the stack. However, your A1.VarA and A2.VarA examples are referring to a field of a reference type - which is stored together with the object on the heap...

Martin Milan
Yes, but the value of that field is int, therefore value type, right?
Petr
@Petr, all fields are contained in the reference type A, which is on the heap.
Alex
+2  A: 

In this case a1.VarA would be on the heap as space for it would have been allocated when you did A a1 = new A().

If you just do int i = 5; in a function that will go on the stack but as you explicitly stated a1 was to be allocated on the heap then all value types associated with it will be placed on the heap

Chris
A: 

Read Jeff Richter's CLR via C# for a complete understanding of this topic.

Alex
A: 

Remember reading in C# in Depth :- Only local variables (the one declared inside method) and method parameter live in stack.Instance variable like varA in above case reside on heap.

Pawan Mishra
Note that local variables which are closed-over locals of a lambda or anonymous method are not stored on the stack in the Microsoft implementation of C#. The same goes for local variables that are in an iterator block.
Eric Lippert
+1  A: 
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)
}
KhanS
+11  A: 

You are asking questions about implementation details, so the answer will depend upon the particular implementation. Let's consider a version of your program that actually compiles:

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

here's what happens on Microsoft's CLR 4.0, running C# 4.0, in Debug mode.

At this point the stack frame pointer has been copied into register ebp:

Here we allocate heap memory for the new object.

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

That returns a reference to a heap object in eax. We store the reference in stack slot ebp-48, which is a temporary slot not associated with any name. Remember, a1 has not been initialized yet.

mov         dword ptr [ebp-48h],eax 

Now we take that reference we just stored on the stack and copy it into ecx, which will be used for the "this" pointer to the call to the ctor.

mov         ecx,dword ptr [ebp-48h] 

Now we call the ctor.

call        FFE8A518 

Now we copy the reference stored in the temporary stack slot into register eax again.

mov         eax,dword ptr [ebp-48h] 

And now we copy the reference in eax into stack slot ebp-40, which is a1.

mov         dword ptr [ebp-40h],eax 

Now we must fetch a1 into eax:

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

Remember, eax is now the address of the heap-allocated data for the thing referenced by a1. The VarA field of that thing is four bytes into the object, so we store 5 into that:

mov         dword ptr [eax+4],5 

Now we make a copy of the reference in the stack slot for a1 into eax, and then copy that into the stack slot for a2, which is ebp-44.

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

And now as you'd expect again we get a2 into eax and then deference the reference four bytes in to write 0x0A into the VarA:

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

So the answer to your question is that references to the object are stored in the stack in three places: ebp-44, ebp-48 and ebp-40. They are stored in registers in eax and ecx. The memory of the object, including its field, is stored on the managed heap. This is all on x86 in the debug build, of Microsoft's CLR v4.0. If you want to know how stuff is stored on the stack, heap and registers in some other configuration, it could be completely different. References could all be stored on the heap, or all in registers; there might be no stack at all. It totally depends on how the authors of the jit compiler decided to implement the IL semantics.

Eric Lippert
It also depends on how the authors of the C# compiler decided to implement the C# semantics. The local variables (`a1` and `a2`) could be implemented as fields in a managed type, leaving just a single reference in each stack frame. I realise bringing this up in a comment to your post invokes thoughts of grandmothers and egg-sucking, but I thought I'd mention it anyway :)
Jon Skeet
@Jon: Indeed. There are very few errors that we produce during the IL generation phase of the compiler; one of them is "too many locals" -- I don't remember what the limit is but it's something like you can't have more than 32K or 64K locals or temporaries in a method. (Obviously real code doesn't have this problem but machine generated code might.) I've often thought that in such cases we should rather than producing an error, just start hoisting them to fields. But it is too obscure a scenario to justify the cost of writing and testing the code.
Eric Lippert
A: 

You missed the method declaration.

Adi
A: 

I am new to C# also. Your question is very important, I also thinked of it. All documentation said, values goes stack and references goes heap, but as the guys above said, its just for the code inside methods. On stair of learning I realize that all programs code begin inside of a method which belong to an instance which belong to heap. So conceptual, the stack is not equal in term with heap like all documentation confuses people. The stack mecanism is found only in a method...

Adi