Well, if you have read my previous post, you should be already clear how memory of ValueTypes and ReferenceTypes are allocated and De-allocated internally in terms of IL. Here in this post, I am going to cover some more concepts behind ValueTypes and ReferenceTypes and what exactly comprises of them.
In my previous post on the series, I have told you that any type that inherits from System.ValueTypes is stored in Stack while any type that is not inherited from System.ValueType is stored in Heap. Well, the statement is not correct totally.
Lets demonstrate this with an example:
public class MyType
int x, y;
public struct MySType
In the above code, I have created two types, 1 being MyType which is a Type directly inherits from System.Object and the other being struct MySType which is derived from System.ValueType. As per our previous discussion you can say, MyType will be allocated in Heap (as it uses newobj of IL) and MySType will be created in Stack being a ValueType (as it uses initobj)
But what about its members?
Class MyType contains two members x and y which are ValueTypes while on the other hand MySType contains a member of MyType which is not a ValueType. So, Where it is going to be allocated?
Well, don't be so much confused about it. Actually while I talked about initobj in my previous post, I have already specified that it will initialize the struct with all its members with default values. Hence when initobj is initialized the value of typ will be assigned to null. At least default constructor for the ValueType does that automatically. But how does it work when we assign an object on it?
Yes, even though MySType is actually a struct, it creates object using newobj IL statement and hence will be stored in Heap. The constructor for MyType will be called and will store the object in heap and get the reference of it in stack. of MySType itself being stored in stack will hold the Reference to the object of MyType in stack, but the actual object does not.
On the contrary, MyType being a Class, it always store the object in heap. Being x and y the member of the type MyType, they are stored in heap itself. As the members of an object comprises the size of the object each of its members will be stored in Heap together.
Thus the actual statement should be "Only local variables are stored in Stack if it is a value type, any other member is stored in heap". Managed environment prefers Heap than stack even though stack is high performance because stack size is limited.
Impact of new operator over Types
Even though we all know about new operator when using reference types, "new" Operator has an important impact overValueTypes which most of people I see does have confusion. Lets clear it with an example.
Lets say I write something like:
MySType s = default(MySType);
s = new MySType();
So here in the code you can see I have created an object of MyType and initialized to its default value which is also implicitly invoke default constructor. In the second line the object of MySType is again created using new oprator. So what does it mean? Does it mean that the memory s is created twice ? Or new operator does not actually create a memory and use up the existing one?
Lets see the IL now:
Basically if you consider the above code, you can see that there is only one reference of local valuetype reference is created, but yet there is two initobj call in the code. So internally there will be two objects been created in your code.
Lets make it more confusing using:
MySType s = new MySType();
And the output to this code is identical to what I showed earlier. Hence in case of ValueTypes you should remember, if you are using any constructor it will first initialize the object using default constructor of struct
and pass the same to the constructor as this. Later it will create another object of S and copy the value of temporary variable to it. This is fact.
Hence if you are using a constructor for your stack, internally IL will create more than 1 object of your struct and assign the initialized object to the local storage in stack. Hence "new" operator on stack always creates a new object even though it is of type Struct.
So you can say all the local variables that you declare inside your own code block which are not member of a class are always allocated in stack? Right ?
Wrong!!! .. It is not always true!!!
If you think of an iterator block, or a lambda expression, or even our new async member function, the local variable here is actually mapped to a class in IL. Hence the local variable that you declare inside an iterator block is actually a member of some type in your compiled assembly. Hence local ValueTypes inside your iterator block is not allocated in Stack
and not for lambda
or async members
So as to conclude this long talk lets put some points:
- Stack and Heap are the primary storage location of any .NET program. (Although it use storage registers too at times).
- Anything that is derived from ValueType and declared locally is sometimes allocated in Stack (with exceptions on iterator block, lambdas, async methods)
- Any type other than ValueType is allocated in Heap and a separate reference variable is created in Stack to use it. The Object will not be deallocated when de-referenced rather will have to rely on GC to collect them.
- Class Members are actually a part of a Type is stored in Heap.
- ValueTypes are not nullable and immutable (with exception to string...) while Reference Types are nullable and generally mutable.
- A Value Type is passed by Value (means will copy itself in new memory) when passed to another method, Reference type will be passed as Reference (while the Reference variable is newly creates which copy the address of the Heap)
I hope you have found good time reading this post.
Keep posting your feedbacks.
Thanks for reading.
Republished from DOT NET TRICKS [47 clicks].
Read the original version here [32134 clicks].