C# Tutorial – Weak References [Advanced]

We all know (hopefully) that C# is a garbage-collected language. In general, what this means is that we as programmers don’t need to free our own memory – the garbage collector will free that memory for us once it is no longer being referenced. Now, of course, garbage collection is a lot more complicated than that, and writing a good garbage collector is actually a relatively hard problem. And the fact that writing a perfect garbage collector is probably impossible is the reason why things like C#’s Weak Reference object exist.
Generally, when you talk about a reference to an object in .NET (and in most other garbage collected languages), you are talking about a “strong” reference. As long as that reference exists, the garbage collector won’t collect the object behind that reference. A weak reference is a special type of reference where the garbage collector can still collect the underlying object, even though you still technically have a reference to it.
The key here is to remember that the garbage collector is not running all the time. As far as we, the programmers of an application, know it is completely random and could kick in at any time. This means that an object only referenced through a weak reference could sit around for long time, or for virtually no time at all (and really, it is even more complicated than trying to figure out the next time the garbage collector will run – because the garbage collector for C# is generational). And, as soon as you copy the reference out of the weak reference variable into a regular reference, the underlying object will no longer be collected (assuming that it hadn’t already been collected), because now you have a strong reference to it.
Ok, enough with this theoretical talk. Let’s get down to some code, and hopefully we can show how this weak reference object is actually useful.
public string _FilePath = "PathToMyImportantFile.dat";
public WeakReference _FileWeakRef = new WeakReference(null);

public List ImportantBigFileContents
{
get
{
List fileStrongRef = _FileWeakRef.Target as List;

if (fileStrongRef == null)
{
using (StreamReader r = new StreamReader(_FilePath))
fileStrongRef
= ParseImportantData(r);

_FileWeakRef.Target = fileStrongRef;
}

return fileStrongRef;
}
}
 
Say I had a large chunk of external data that would be handy to keep in memory, but really isn’t used very often (or maybe it is used a bunch in bursts). This is exactly what the weak reference object is good for. In the code above, I am storing the parsed version of some “Important Data” in a WeakReference variable. What this means is that when someone tried to access the ImportantBigFileContents, the parsed data may or may not still be in memory.
So first, we try and pull the reference out of the _FileWeakRefobject. If that is null, we load the data from the file, parse it, store it and hand it back. Otherwise we hand back what we got from the weak reference variable. So this means that sometimes, the data will be in memory, but other times the code will have to go out and reload it. This doesn’t make sense to do in all cases (or even in many cases), but if the data is accessed in bursts, and you really didn’t want to keep it in memory all the time anyway, this gives you what you need with very little extra work (the garbage collector does your management for you).
Now, there is a couple of common tear-your-hair-out mistakes that can be made when using weak references. See if you can tell what is wrong with the code below (and remember, the garbage collector might run at any time):
public List ImportantBigFileContents
{
get
{
if (_FileWeakRef.Target == null)
using (StreamReader r = new StreamReader(_FilePath))
_FileWeakRef.Target = ParseImportantData(r);

return _FileWeakRef.Target as List;
}
}
 
Figure it out? Yup, the garbage collector could run during this property access – cleaning up the memory for this weak ref object just as we were about to hand it back:
public List ImportantBigFileContents
{
get
{
if (_FileWeakRef.Target == null)
using (StreamReader r = new StreamReader(_FilePath))
_FileWeakRef.Target = ParseImportantData(r);

/* Garbage colllector could run right here. Whoops!*/

return _FileWeakRef.Target as List;
}
}
 
So always remember to pull the reference out into a strong (or regular) reference before you do any manipulation or checking – otherwise, stuff could change out right from under you.
Well, that’s all for an intro to the weak reference object. For you Java developers out there, you actually have an equivalent (and it works almost exactly the same) – the WeakReferenceclass.

Leave a comment