In the context of C#:
Let's assume the following code ...
public class Foo
{
MrObject object1;
MrObject object2;
MrObject object3;
MrObject object4;
MrStruct struct1;
MrStruct struct2;
MrStruct struct3;
MrStruct struct4;
int i;
public Foo()
{
object1 = new MrObject(1); /// Alpha
object2 = new MrObject(2); /// Bravo
object3 = new MrObject(3); /// Charlie
object4 = new MrObject(4); /// Delta
i = 1;
InitializeComponent(); // POINT A
ByValByRef(object1, ref object2);
}
public void ByValByRef(MrObject o1, ref MrObject o2)
{
o1.foo = 5; // POINT B
o1 = object3; // POINT C
o2.foo = 6; // POINT D
o2 = object4; // POINT E
return;
}
My understanding is that at // POINT A - you have created four instances of MyObject. Those four instances live in the heap. - you have four references to those instances. QUESTION 1: Where do they live?
My understanding is that at // POINT B
- you have created a new reference to 'Alpha' named 'o1'. That reference is added to and lives on the stack. When the method returns, that reference is removed.
- you have a reference named 'o2'. QUESTION 2: Is this a new reference that is added to and lives on the stack and refers to the 'object2' reference? Or is 'o2' simply an alias maintained in code (and not in memory) and in fact there is still only one reference in memory to 'Bravo' called 'object2' and when this code actually executes, 'o2.foo' is treated as 'object2.foo'?
My understanding is that at // POINT A - you have created four instances of MyObject. Those four instances live in the heap.
Right. Memory has been allocated on the heap for each object.
you have four references to those instances. QUESTION 1: Where do they live?
When you created the objects, you assigned (a reference to) each one to different field on the Foo
object. Those fields "live" in the memory allocated for the Foo
object, which is almost certainly somewhere on the heap as well.
My understanding is that at // POINT B - you have created a new reference to 'Alpha' named 'o1'. That reference is added to and lives on the stack. When the method returns, that reference is removed.
Yes, at POINT B, the parameter o1
also stores a reference to the MrObject
that was passed in as an argument. o1
lives on the short-term store, which will in general be "the stack". When the method ends, the space on the short-term store used for o1
will be made available for other use, and the garbage collector will no longer consider o1
as a known live root.
you have a reference named 'o2'. QUESTION 2: Is this a new reference that is added to and lives on the stack and refers to the 'object2' reference? Or is 'o2' simply an alias maintained in code (and not in memory) and in fact there is still only one reference in memory to 'Bravo' called 'object2' and when this code actually executes, 'o2.foo' is treated as 'object2.foo'?
And now we get to the tricky part of your question. o2
is a reference of a different sort. It is not a reference to an object on the heap, but rather it is effectively an alias (as you surmise) to the object2
field on the Foo
object. o2
still occupies space on the short-term store, though, since it is still an argument to a method.
As an aside, the fact there is "only one reference in memory to Bravo
" is not what you should be worried about. References that are reachable from known GC roots are what determine whether or not a particular object can be safely collected.