1. Class, object and reference
Recall that class is nothing more than a blueprint that describes how an instance of this type will look and feel in memory. And they are defined within a code file (.cs in C#).
After a class has been defined, you may allocate any number of objects using new keyword. Understand, however, that the new keyword returns a reference to the object on the heap, not the object itself. And if you declare the reference variable as a local variable in a method scrop, it is stored on the stack for further use in your application.
2. The basics of object lifetime
When you build c# application, .net runtime will take care of managed heap without your intervention.
The garbage collector will destory any object when it is no longer needed and unreachable by any part of your code base.
static void MakeCar() { Car myCar = new Car (); }
Notice that Car reference myCar has been created directly within the method MakeCar() and has not been passed outside of the defining scope. Thus, once this method call completes, the myCar reference is no longer reachable, the object is now a candidate for garbage collection. However, you cannot guarantee that this object will be reclaimed from memory immediately after call has completed. It will be safely destroryed when CLR perform next garbage collection.
2.1 setting object reference to null
static void MakeCar() { Car myCar = new Car (); myCar = null; }
When you assign object references to null, the compiler ensures the reference no longer points to any object. However, you must understand the process does not in any way force the garbage collector to fire up and remove the object from the heap.
3. Understanding object generations
When CLR is attempting to locate unreachable objects, it does not literally examine each and every object placed on the manged heap. It would obviously involve considerable time.
To help optimize the process, each object on the heap is assigned to a specific generation. The longer an object existed on the heap, the more likely it is to stay here.
Generation 0: new allocated object that has never been marked for collection
Generation 1: Identifies an object that has survived a garbage collection.
Generation 2: Identifies an object that has survived more than one sweep of the garbage collector.
The garbage collector will investigate all generation 0 first, then generation 1 and 2.
The bottom line is that by assigning a generational value to objects on the heap, newer objects will be removed quickly.
4. System.GC
The mscorlib.dll assemlby provides a class type named System.GC that allows you to interact with garbage collector. Do be very aware that you will seldom need to make use of this class directly in your code.
4.1 Forcing a garbage collection
There are two common situations where you might consider interacting with the collection process:
(1) your application is about to enter into a block of code that you don‘t want interrupted by a possible garbage collection.
(2) your application has just finished allocating an extremely large number of objects and you want to remove as much of the acquired memory as soon as possible
You could trigger a garbage collection as follows:
public static void Main (string[] args) { //force garbage collection GC.Collect(); GC.WaitForPendingFinalizers (); }
When you manually force a garbase collection, you should always make a call to GC.WaitForPendingFinalizers(). With the approach, you can rest assured that all finalizable objects have had a change to perform any necessary cleanup before your program continues. Under the hood, it will suspend the calling thread during the collection process.
GC.Collect() can also be supplied a numerical value that identifies the oldest generation on which a garbage collection will be performed.
public static void Main (string[] args) { //only investigate generation 0 objects GC.Collect(0); GC.WaitForPendingFinalizers (); }
Also, the Collect() method can also be passed in a value of the GCCollectionMode enumeration as a second parameter.
public static void Main (string[] args) { //force it immediately GC.Collect(0, GCCollectionMode.Forced); GC.WaitForPendingFinalizers (); }
5. Building Finalizable Objects
In .net framework, System.Object class defines a virtual method named Finalize() which is used to perform any necessary clearnup logic for your type. The majority of your c# classes will not require any explicit cleanup logic or a custom finalizer. The only time you would need to design a class that can clean up is when you are using unmanaged resources such as using PInvoke or COM objects.
6. Disposable objects
As an alternative to overriding Finalize(), you class could implment the IDisposable inteface.
public interface IDisposable { void Dispose(); }
When you do implment the IDisposable, the object user manually calls dispose before allowing the object reference to drop out of sceop. In this way, an object can perform immediate cleanup without waiting for garbage collector or finalizarion queue.
public static void Main (string[] args) { FileStream fs = new FileStream ("test.txt", FileMode.OpenOrCreate); fs.Close (); //both method do the same thing fs.Dispose (); }
6.1 C# using keyword
When you are handling a managed object that implent IDisposable intefarce, it is common to make use of stucture of exception-handling to ensure Dispose() is called.
public static void Main (string[] args) { FileStream fs = new FileStream ("test.txt", FileMode.OpenOrCreate); try{ //use of fs } finally { fs.Close (); //both method do the same thing fs.Dispose (); } }
And to achieve the same resule in a much less obtrusive manner, C# support using keyword.
public static void Main (string[] args) { //dispose is called automatically using (FileStream fs = new FileStream ("test.txt", FileMode.OpenOrCreate)) { } }
In fact, at the backgourd, the compiler translate using statement to a proper try, catch and the calling dispose() methods.