WeakReference comes very handy when working with Large objects. In this application, I have demonstrated how you could easily use this class to improve the performance of your application.
Introduction
We all know .NET objects deallocates memory using Garbage Collection.
Garbage collection is a special process that hooks in to the object
hierarchy randomly and collects all the objects that are not reachable
to the application running. Let us make Garbage collection a bit clear
before moving to the alternatives.
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.
Is WeakReference 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 Strong Reference, as
by the definition of Strong Reference, the objects that are reachable to
the GC are called Strong Reference 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.
In such a case, even though the object still in the application
memory area, you still need to create another object. Here comes the
use of WeakReference.
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<string>. Now lets create another class to show the actual implementation of the WeakReference class.
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 the 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 G
C.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.
Conclusion
I hope the article will help you to workaround on large objects. Thank you for reading.
Looking for your feedback.