The Stack is responsible of what’s been “called” ,is self-maintaining, meaning that it basically takes care of its own memory management. First in last out – “FILO” .Stack as a series of boxes stacked one on top of the next.When we’re done with the top box (the method is done executing) we throw it away and proceed to use the stuff in the previous box on the top of the Stack.The Stack is like the Stack of shoe boxes in the closet where we have to take off the top one to get to the one underneath it.
Heap is responsible for keeping track of our objects, its purpose is to hold information so anything in our Heap can be accessed at any time.there are no constraints as to what can be accessed like in the Stack. we can grab what we need quickly.The Heap,handled by Garbage collection (GC), which deals with how to keep the Heap clean.
We have four main types of things we’ll be putting in the Stack and Heap as our code is executing: Value Types, Reference Types, Pointers, and Instructions.
Value Types: from System.ValueType
bool , byte,char,decimal,double,enum,float, struct..
Reference Types: and inherit from System.Object
class ,interface,delegate,object,string
Pointers: Reference to a Type
We don’t explicitly use Pointers, they are managed by the Common Language Runtime (CLR). A Pointer (or Reference) is different than a Reference Type in that when we say something is a Reference Type, it means we access it through a Pointer.A Pointer is a chunk of space in memory that points to another space in memory.A Pointer takes up space just like any other thing that we’re putting in the Stack and Heap and its value is either a memory address or null.
A Reference Type always goes on the Heap
Value Types and Pointers always go where they were declared.
If the value type is a poco and doesn’t implement some object , than it will enter the Stack.
public int AddFive(int pValue)
{
int result;
result = pValue + 5;
return result;
}
But if we are dealing with Pointers even though the members are primitive types it will be entered into stack and point to the object in the heap.
public class MyInt
{
public int MyValue;
}
public int ReturnValue2()
{
MyInt x;
x.MyValue = 3;
MyInt y;
y = x;
y.MyValue = 4;
return x.MyValue;
}
Each local variable is also located in Stack as being sent to the methods.
The methods have a GOTO reference (instructions) which direct them where to return after the method completed.
But if we have a big data primitive member, variable , such as some big struct it can overload the Stack.
The resolution is to use ref
Sending Value Type by ref
public void Go()
{
MyStruct x = new MyStruct();
DoSomething(ref x);
}
public struct MyStruct
{
long a, b, c, d, e, f, g, h, i, j, k, l, m;
}
public void DoSomething(ref MyStruct pValue)
{
// DO SOMETHING HERE….
}
that way the reference is only entered once in the Stack and if we’d preform that method 1000 times it’ll only add some more pointers to the reference location in the stack.
If our object is big it makes a big difference.
Important – when you are changing the local variable inside the Method where you send value by reference you are actually changing the parameter outside that method also.
Sending Reference Type as parameter
both x and pValue – are set into Stack as a Pointer to Object created in the Heap.
public void Go()
{
MyInt x = new MyInt();
x.MyValue = 2;
DoSomething(x);
Console.WriteLine(x.MyValue.ToString());
}
public void DoSomething(MyInt pValue)
{
pValue.MyValue = 12345;
}
So to conclusion , the reference(the definition of the object) to the object going to the Stack while the Object itself enters the Heap.
If the initialization of the reference is reinitialized to some other Object than accordingly the reference(hence the Pointer) will point to the new Object that will also be created in the Heap.
For the objects inside the objects to be unique we would want to implement the IClonable interface initializing the inner object each time we need it . Otherwise we can end up pointing out the same object from different parent objects.
We would implement public object Clone() and will get the object returned as MyObjType .
GC –
JIT and CLR are using the heap – list of used recourses and all that connected to them is mapped to a graph. GC gets the list of "root" object references to keep from just-in-time (JIT) compiler and common language runtime (CLR) and then recursively searches object references to build a graph of what should be kept
Roots consist of:
- Global/Static pointers. One way to make sure our objects are not garbage collected by keeping a reference to them in a static variable.
- Pointers on the stack. We don’t want to throw away what our application’s threads still need in order to execute.
- CPU register pointers. Anything in the managed heap that is pointed to by a memory address in the CPU should be preserved
- Each unneeded object (one that didn’t entered the graph will be erased and the objects will reform a new memory allocation. Each deletion is taking time so it’s better to minimize the amount of created objects.
There are situations where the GC needs to execute code to clean up non-managed resources such as files, database connections, network connections, etc. One possible way to handle this is through a finalizer.
class Sample
{
~Sample()
{
// FINALIZER: CLEAN UP HERE
}
}
What it does – inserts the object to a finalization queue , when GC sees that the object is no longer needed it inserts it into a freachable queue , and only after the finalizer is executed GC will approach to remove it. (at the next generation only) – so actually it’s prolonging the life of the object and maximize the usage of the heap memory , so it’s better to implement the finalizer when and only we really need it.
A better practice is to be sure to clean up non-managed resources. As you can imagine, it is preferable to explicitly close connections and use the IDisposable interface for cleaning up instead of a finalizer where possible.
public class ResourceUser : IDisposable
{
#region IDisposable Members
public void Dispose()
{
// CLEAN UP HERE!!!
}
#endregion
}
If you implemented Dispose and used the using , at the end the Dispose will be executed and GC will know to clean up the unneeded resource.
public static void DoSomething()
{
using (ResourceUser rec = new ResourceUser())
{
// DO SOMETHING
} // DISPOSE CALLED HERE
}
// DON’T ACCESS rec HERE
Statics and heap overflowed
1)If we use a static member inside a static method , we better lock the initialization to prevent access to the same resource by different threads.
class Counter
{
private static int s_Number = 0;
public static int GetNextNumber()
{
lock (typeof(Counter))
{
int newNumber = s_Number;
// DO SOME STUFF
newNumber += 1;
s_Number = newNumber;
return newNumber;
}
}
}
The best approach with statics will be a singleton pattern
public class Earth {
private static Earth _instance = new Earth();
private Earth() { }
public static Earth GetInstance() { return _instance; }
}
Summary of
http://www.c-sharpcorner.com/UploadFile/rmcochran/csharp_memory01122006130034PM/csharp_memory.aspx