BlackWaspTM

This web site uses cookies. By using the site you accept the cookie policy.This message is for compliance with the UK ICO law.

.NET Framework
.NET 1.1

Weak References

.NET objects are held in memory, within in the managed heap, until all references are released and the garbage collector removes them. Weak references provide an alternative type of object reference, which may be garbage collected whilst still active.

Weak and Strong References

When you create an object in a .NET application you generate a reference to that object. You might later copy this reference many times into other variables, fields or properties, creating many references. Whilst any one of these references remains reachable, the garbage collector is prevented from removing that object from the managed heap. When no active references remain, the garbage collector is free to release the memory. This garbage collection system means that, most of the time, you don't need to worry about memory management or the risk of memory leaks.

There are situations where it is a good idea to allow the garbage collector to release objects whilst references to them still exist. A good example is a large cached object that might be used later but that consumes a large amount of memory. You might load a substantial amount of information from a database and retain that data in case it needs to be accessed again. However, if memory pressure is high, you might want to allow the garbage collector to free the memory, knowing that you can recreate the cached data later. This can be achieved with weak references.

A weak reference is held in an object of the WeakReference type. This class allows you to wrap an existing, strong reference, in a new object. If the garbage collector runs whilst you have only weak references to an object, that object can be collected and its memory made available. When you need to use the wrapped object you can obtain a strong reference to it, which prevents garbage collection until that strong reference becomes unreachable.

Creating a Weak Reference

To create a weakly referenced object you pass the object to wrap into the WeakReference's constructor. We can see this in the code sample below. This code represents the use of some cached data. In this case the data being wrapped is an array containing one million integers.

The Main method calls GenerateLargeArray, which creates the array and holds it in the _cache field as a WeakReference. The wrapped array is created and populated quickly but it could be retrieved from a remote service that introduces a delay. This would be a good candidate for a weak reference because it uses a significant amount of memory and can be recreated easily after it has been garbage collected.

class Program
{
    static WeakReference _cache;

    static void Main(string[] args)
    {
        GenerateLargeArray();
    }

    static void GenerateLargeArray()
    {
        int[] values = new int[1000000];
        for (int i = 0; i < 1000000; i++)
        {
            values[i] = i;
        }
        _cache = new WeakReference(values);
    }
}

Obtaining a Strong Reference from a Weak Reference

To use the object held by the weak reference you read the Target property. This returns a simple object, so needs to be cast to the original type before you can access its methods and properties. Once you have a strong reference, the wrapped object will not be garbage collected.

int[] strongReference = _cache.Target as int[];

If the weak reference has been garbage collected before you try to read the wrapped object, the Target property will return null. You should check for this before trying to access the returned object's members.

GC.Collect();
int[] strongReference = _cache.Target as int[];

if (strongReference != null)
{
    Console.WriteLine(((int[])strongReference).Length);
}
else
{
    // Deal with garbage collected weak reference
}

You must take care when checking for a null Target that you obtain the strong reference first then compare this to null. Checking the Target property before obtaining the strong reference introduces a potentially nasty bug. We do this in the code shown below. In most cases this code will work as intended. However, there is a very small chance of throwing a NullReferenceException. The exception occurs if the garbage collector runs after the if statement detects a non-null target but before the Console.WriteLine command is reached. In such cases when the Length property of the array is accessed the Target property of the WeakReference will be null. This creates an intermittent bug that could be very difficult to find.

if (_cache.Target != null)
{
    Console.WriteLine(((int[])_cache.Target).Length);  // Potential bug
}

IsAlive Property

The WeakReference class includes a property, named IsAlive, which returns a Boolean value indicating whether or not the target object has been garbage collected. This property is useful if you want to detect a released object, as in the sample code below:

bool hasBeenCollected = !_cache.IsAlive;

The problem with IsAlive is that it makes it easy to introduce intermittent faults, similar to the one seen earlier, if you react to the property being true. The code below shows such a bug. If the IsAlive property returns true, the Console.WriteLine command executes. However, if the garbage collector runs after the if statement but before the message is generated, the weak reference's target will have become null causing a NullReferenceException.

if (_cache.IsAlive)
{
    Console.WriteLine(((int[])_cache.Target).Length);
}
30 November 2012