Garbage Collection Algorithm
In .NET, every object is allocated using Managed Heap. We call it managed as every object that is allocated within the .NET environment is in explicit observation of GC. When we start an application, it creates its own address space where the memory used by the application would be stored. The runtime maintains a pointer which points to the base object of the heap. Now as the objects are created, the runtime first checks whether the object can be created within the reserved space, if it can it creates the object and returns the pointer to the location, so that the application can maintain a Strong Reference to the object. I have specifically used the term Strong Reference for the object which is reachable from the application. Eventually the pointer shifts to the next base address space.
When GC strikes with the assumption that all objects are garbage, it first finds all the Strong References that are global to the application, known as Application Roots and go on object by object. As it moves from object to object, it creates a Graph of all the objects that it finds from the application Roots, such that every object in the Graph is unique. When this process is finished, the Graph will contain all the objects that are somehow reachable to the application. Now as the GC already identified the objects that are not garbage to the application, it goes on Compaction. It linearly traverses to all the objects and shifts the objects that are reachable to non reachable space which we call as Heap Compaction. As the pointers are moved during the Heap compaction, all the pointers are reevaluated again so that the application roots are pointing to the same reference again.
WeakReference as an Exception
On each GC cycle, a large number of objects are collected to release the memory pressure of the application. As I have already stated, that it finds all the objects that are somehow reachable to the Application Roots. The references that are not collected during the Garbage Collection are called StrongReference, as by the definition of StrongReference, the objects that are reachable to the GC are called StrongReference objects.
This creates a problem. GC is indeterminate. It randomly starts deallocating memory. So say if one have to work with thousand bytes of data at a time, and after it removes the references of the object it had to rely on the time when GC strikes again and removes the reference. You can use GC.Collect to request the GC to start collecting, but this is also a request.
Now say you have to use the large object once again, and you removed all the references to the object and need to create the object again. Here comes huge memory pressure. So in such situation you have :
- Already removed all references of the object.
- Garbage collection didnt strike and removed the address allocated.
- You need the object again.
Download Sample Application - 27 KB
Types of WeakReference
WeakReference can be of two types :
- Short : short WeakReference loses the reference when the GC is collected. In our case we have used short WeakReference.
- Long : Long WeakReference is retained even the objects Finalize method is called. In this case the object state cannot be determined. We pass trackResurrection to true in the constructor of WeakReference which defaults false, to track the object even though the Finalize method is called.
A WeakReference object takes the Strong Refernece of an object as argument which you can retrieve using Target property. Let us look into the example :
To demonstrate the feature, lets create a class which uses a lot of memory.
public class SomeBigClass : List<string> { public SomeBigClass() { this.LoadBigObject(); } private void LoadBigObject() { for (int i = 0; i < 100000; i++) this.Add(string.Format("String No. {0}", i)); } }
Clearly the SomeBigClass is a list of 100000 strings. The code looks very straight forward, as I have just created an alternative to define List
public class WeakReferenceUsage { WeakReference weakref = null; private SomeBigClass _somebigobject = null; public SomeBigClass SomeBigObject { get { SomeBigClass sbo = null; if (weakref == null) //When it is first time or object weakref is collected. { sbo = new SomeBigClass(); this.weakref = new WeakReference(sbo); this.OnCallBack("Object created for first time"); } else if (weakref.Target == null) // when target object is collected { sbo = new SomeBigClass(); weakref.Target = sbo; this.OnCallBack("Object is collected by GC, so new object is created"); } else // when target object is not collected. { sbo = weakref.Target as SomeBigClass; this.OnCallBack("Object is not yet collected, so reusing the old object"); } this._somebigobject = sbo; //gets you a strong reference return this._somebigobject; } set { this._somebigobject = null; } } # region GarbageEvent public event Action<string> CallBack; public void OnCallBack(string info) { if (this.CallBack != null) this.CallBack(info); } # endregion }
In the above class, we define a reference of WeakReference as weakref, which holds the object of SomeBigClass. Now the property SomeBigClass has few logic defined within it. It uses the existing WeakReference.Target to fetch the existing object. If the Target is null, the object will again be recreated and stored within the WeakReference Target again.
WeakReference serves as an exception to the existing GC algorithm. Even though the object is reachable from the application, it is still left for GC collection. So if GC strikes, it will collect the object of SomeBigClass and the WeakReference.Target will lose the reference.
Demonstration of Code
To demonstrate the class, lets create a Console application and create the object of WeakReferenceUsage. Lets the example class looks like :
static void Main(string[] args) { WeakReferenceUsage wru = new WeakReferenceUsage(); wru.CallBack += new Action<string>(wru_CallBack); while (true) { //Access somebigclass foreach (string fetchstring in wru.SomeBigObject) Console.WriteLine(fetchstring); //fetch complete. wru.SomeBigObject = null; GC.Collect(); // request to collect garbage. ConsoleKeyInfo info = Console.ReadKey(); if (info.Key == ConsoleKey.Escape) break; } } static void wru_CallBack(string obj) { Console.WriteLine(obj); Console.Read(); }
Here in the Main method, I have created an object of WeakReferenceUsage, and registered the callback so that whenever we try to retrieve the object, the message will be displayed in the console.
By setting
wru.SomeBigObject = null;
GC.Collect(); // request to collect garbage.
Will destroy the strong application reference and hence the object will be exposed for Garbage Collection. The call GC.Collect will request the garbage collection to collect.
Thus on First run you will see :
The object is created for the first time.
After you fetch all the data, you might either receive the the second message, saying that the object is not yet collected and the object is fetched from the existing WeakReference, or if you wait for a long time, you might receive the 3rd message which says that the object is Collected by GC and object is recreated.
Download Sample Application - 27KB
I hope the article will help you to workaround on large objects. Thank you for reading.
Looking for your feedback.
No comments:
Post a Comment
Please make sure that the question you ask is somehow related to the post you choose. Otherwise you post your general question in Forum section.