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 4.5+

Weak References in .NET 4.5

The WeakReference class provides a way to create weakly referenced objects that may be released by the garbage collector when memory pressure is high. An improved, generic version of this class was introduced in the .NET framework version 4.5.

WeakReference<T> Class

In yesterday's article I described the WeakReference class. This allows you to wrap an object of any type to turn it into a weakly-referenced object. The garbage collector can remove such objects from the managed heap when memory is low, as long as no strong references remain. NB: If you haven't read the previous article you should read it before continuing, as the weak reference concepts are not repeated here.

The WeakReference class can be very useful but, when used incorrectly, it makes it easy to introduce intermittent bugs into your software. These relate to the incorrect use of the Target property, which holds the wrapped object, and the IsAlive property, which can be used to find out if the reference has been garbage collected. I described the two problems in the earlier article so I won't repeat them here.

Version 4.5 of the .NET framework introduces a new class for holding weak references that reduces the risk of introducing defects. It is a generic class, so removes the use of casting required by WeakReference. The class is named, WeakReference<T>.

Creating a Weakly Referenced Object

You can create a new weak reference to an object by passing that object to the parameter of a WeakReference<T> constructor. This works in an identical fashion as with the non-generic version of the class. We can see this with some small modifications to the sample code from yesterday's article. If you compare the code below to that from the earlier article, you can see that only the type of the _cache object has changed.

class Program
{
    static WeakReference<int[]> _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<int[]>(values);
    }
}

Obtaining a Strong Reference

Obtaining a strong reference to the wrapped object works differently than with the non-generic WeakReference type. The Target and IsAlive properties no longer exist. Instead, the two pieces of information that they provide are combined in a single operation via the TryGetValue method. This method returns a Boolean that is false if the weakly-referenced object has been garbage collected and true if not. If the object is still alive, a strong reference to it is returned using an output parameter.

The code below passes the strongReference variable to the output parameter. It also checks the return value. If the result of the call is true, we display the length of the array.

int[] strongReference;
if (_cache.TryGetTarget(out strongReference))
{
    Console.WriteLine(strongReference.Length);
}

Changing the Wrapped Reference

As there is no longer a Target property, we need an alternative approach to change the weak reference's wrapped object. You can now use the SetTarget method for this purpose and provide the new reference as the only argument. You can see this in the final code sample, which shows another version of the example program. This time we initialise the cache object in the field's declaration, wrapping a null reference. Instead of using the constructor and generating a new weak reference for each call to GenerateLargeArray, we use SetTarget to reuse the same WeakReference<T> instance when we regenerate the array.

class Program
{
    static WeakReference<int[]> _cache = new WeakReference<int[]>(null);

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

        int[] strongReference;
        if (_cache.TryGetTarget(out strongReference))
        {
            Console.WriteLine(strongReference.Length);
        }
    }

    static void GenerateLargeArray()
    {
        int[] values = new int[1000000];
        for (int i = 0; i < 1000000; i++)
        {
            values[i] = i;
        }
        _cache.SetTarget(values);
    }
}
1 December 2012