Click here to Skip to main content
13,298,551 members (42,800 online)
Click here to Skip to main content
Add your own
alternative version


22 bookmarked
Posted 13 Oct 2009

WeakReferences, GCHandles, and WeakArrays

, 13 Oct 2009
Rate this:
Please Sign up or sign in to vote.
This article shows the differences of WeakReferences and GCHandles and how to implement a WeakArray.


For those who don't know yet, Weak References are references to objects that still allow such objects to be collected, then becoming null. By default, every reference used when you program is a Strong-Reference. That is, such a reference will not allow the other object to die, considering such a reference is not "dead" itself.

But the purpose here is not to explain exactly how Weak References work. It's to explain the difference between Weak References and GCHandles, and why GCHandles can be better in some situations.

What is a GCHandle?

It looks redundant, but internally, the WeakReference class uses some GCHandle internal methods to do its job. But we can simplify this by saying that the WeakReference is a GCHandle wrapper with a destructor.

The GCHandle has other uses but, for the weak-reference part, it is a ValueType "unsafe" reference to the object. I say "unsafe" because it does not require you to be in unsafe mode to be used, but its possibility of memory leaks makes it unsafe.

Why exactly is it unsafe?

Because it does not have a destructor. You may think that allowing the object to die will never cause memory-leaks, but the reference itself will be lost forever if you forget to free it. It does not leak the same amount of memory as leaking an entire object, but will be a memory-leak the same way. Using a Weak Reference will avoid this completely.

So, why would anyone ever consider using it?

Performance. When you create only one Weak Reference to a large image, for example, you will never notice any performance penalty for using the WeakReference class. But, think about creating a Cache framework, which will store thousands of "cached objects".

If you use Weak References, you will in fact:

  • Have 10000 strong-references to the Weak References;
  • Have 10000 GCHandles inside these Weak References;
  • And then have the 10000 objects (or nulls for those not used or already collected).

One important thing here is:

Weak References have destructors. And so, when the memory is collected, the Weak References themselves will live to the next generation. If this is a Generation 0 collection, the WeakReference objects will live for the Generation 1 collection. We can have many Generation 0 collections before the Generation 1 collection arrives.

If we use GCHandles directly, we will have:

  • 10000 GCHandles;
  • And then have the 10000 objects (or nulls for those not used or already collected).

Well, at this point, I think I will explain Weak Arrays. .Net does not come with WeakLists, WeakArrays, or anything similar. To implement lists, we internally need to have arrays, and the first step is to implement WeakArrays.

For example:

WeakReference[] weakReferenceArray = new WeakReference[10000];

The previous line creates an array of weak references (not a WeakArray). It has 10000 positions, to store Weak References, but those Weak References will need to be initialized some-time. I will initialize them immediately with:

for (int i=0; i<10000; i++)
  weakReferenceArray[i] = new WeakReference(null);

Well, I still don't have any real objects here, but I already have an array with 10000 strong-references to 10000 Weak Reference objects.

Let's see the GCHandle version:

GCHandle[] gcHandles = new GCHandle[10000];
for(int i=0; i<10000; i++)
  gcHandles[i] = GCHandle.Alloc(null, GCHandleType.Weak);

Well, the code is very similar but, in fact, we have only an array with 10000 references that are already GCHandles (as they are value types). We don't have 10000 objects with 10000 finalizers. But, that's the main point. If we simple do this, even if the array is collected, the 10000 "handles" will be lost. So, different from the first example, we need to implement a destructor. Of course, we can create only one destructor to free all handles. This is the main advantage.

So, our first version of the WeakArray class:

public class WeakArray
  private GCHandle[] fArray;
  public WeakArray(int length)
    fArray = new GCHandle[length];
    for (int i=0; i<length; i++)
      fArray[i] = GCHandle.Alloc(null, GCHandleType.Weak);
    int count = fArray.Length;
    for(int i=0; i<count; i++)
  public int Length
      return fArray.Length;
  public object this[int index]
      return fArray[index].Target;
      fArray[index].Target = value;

Well, now we have an almost leak-free WeakArray. Why almost? If we can't allocate the requested length, we will generate an exception. But, that's not the problem, the user may treat the exception. The destructor will also be invoked and we don't test if the array or the references are valid. Calling Free on an unitialized handle will throw an exception. But, still, if the constructor was completed, the finilizer is OK.

Why am I showing the incomplete code? Because it looks like it is fine. That's the most important thing to be aware of. Now, let's see the right destructor:

  if (fArray == null)
  int count = fArray.Length;
  for(int i=0; i<count; i++)
    GCHandle gcHandle = fArray[i];
    if (!gcHandle.IsAllocated)

First thing: Check if the array is null. If it is, then we can simple return, as we don't allocate the array.

Second thing: Loop over all items. On we reach the first one that is not allocated, we stop. Why? Because we allocate them in order. If the 10th is not allocated, 11th, 12th, and the rest will also not be allocated.

Now, I can say we have a working WeakArray. But, is it really complete? The answer is No.

We didn't implement the Dispose method. The idea is to have a Dispose method for any method that has a destructor. But, it is not that simple. To guarantee a non-leaking WeakArray which disposes, we would need to make the array thread-safe (or will need to make another class, like a ThreadSafeWeakArray). The actual one doesn't need anything special to be thread-safe, as the GCHandle itself is thread-safe, and the full allocation and deallocation are done only in the constructor and destructor, which will always run on a single thread. We could make the array only initialize the handles when they are first needed. But this will, again, need to care about Thread-Safety. And finally, if we want to make the arrays to use them in lists, we need to think about how lists work: Initially, they allocate some amount of space. Let's say 8. Then, when we add the ninth item, they are reallocated, to the size of 16, and we fill the item 9.

Well, how will the reallocation work?

If we have a WeakArray of 8 and we allocate another WeakArray of 16, we will have 24 weak-references. Maybe we can simply create a "Resize" method that reallocates the fArray object, and then we only initialize GCHandles from 9 to 16. But, of course, I am again forgetting the "allocate only when needed" behavior. Complex, isn't? I will leave this for another article. For now, this simple WeakArray implementation is the best one to be shown. At least you will only have one finalizer (and one real object) for all the weak-GCHandles, instead of having one object and finalizer for each.


This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


About the Author

Paulo Zemek
Engineer YouTube
United States United States
I started to program computers when I was 11 years old, as a hobbyist, programming in AMOS Basic and Blitz Basic for Amiga.
At 12 I had my first try with assembler, but it was too difficult at the time. Then, in the same year, I learned C and, after learning C, I was finally able to learn assembler (for Motorola 680x0).
Not sure, but probably between 12 and 13, I started to learn C++. I always programmed "in an object oriented way", but using function pointers instead of virtual methods.

At 15 I started to learn Pascal at school and to use Delphi. At 16 I started my first internship (using Delphi). At 18 I started to work professionally using C++ and since then I've developed my programming skills as a professional developer in C++ and C#, generally creating libraries that help other developers do their work easier, faster and with less errors.

Want more info or simply want to contact me?
Take a look at:
Or e-mail me at:

Codeproject MVP 2012, 2015 & 2016
Microsoft MVP 2013-2014 (now I work at Microsoft so I can't be a Microsoft MVP anymore)

You may also be interested in...

Comments and Discussions

GeneralWe want the next article! + question Pin
I'm Chris24-Mar-10 5:38
memberI'm Chris24-Mar-10 5:38 
GeneralRe: We want the next article! + question Pin
Paulo Zemek24-Mar-10 16:51
memberPaulo Zemek24-Mar-10 16:51 
GeneralRe: We want the next article! + question Pin
I'm Chris24-Mar-10 22:24
memberI'm Chris24-Mar-10 22:24 
GeneralRe: We want the next article! + question Pin
Paulo Zemek25-Mar-10 14:44
memberPaulo Zemek25-Mar-10 14:44 
QuestionAbout finalizer Pin
Puchko Vasili15-Oct-09 3:16
memberPuchko Vasili15-Oct-09 3:16 
AnswerRe: About finalizer Pin
Paulo Zemek15-Oct-09 4:35
memberPaulo Zemek15-Oct-09 4:35 
GeneralSounds interesting Pin
supercat914-Oct-09 7:55
membersupercat914-Oct-09 7:55 
GeneralRe: Sounds interesting Pin
Paulo Zemek14-Oct-09 8:19
memberPaulo Zemek14-Oct-09 8:19 
GeneralRe: Sounds interesting Pin
supercat914-Oct-09 12:23
membersupercat914-Oct-09 12:23 
GeneralRe: Sounds interesting Pin
Paulo Zemek14-Oct-09 15:42
memberPaulo Zemek14-Oct-09 15:42 
I am plan to posting a new article on the subject soon.
I will really make it different. To be honest, the Resize method of the array looks like an extension method. It simple "creates a new array if needed".

Considering the expected behavior, I really think that the WeakArray must change it's size.

About Interlocked... well, I can't use interlocked on arrays. As I remember, interlocked only works with data directly. Trying to use interlocked with an item in the array will not work, so, it is not an option.
Also, custom locking, even slower, is easier to understand. And, considering that we will be reutilizing an object instead of loading it again, I think the lock "waste of time" is not so bad.

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

Permalink | Advertise | Privacy | Terms of Use | Mobile
Web03 | 2.8.171207.1 | Last Updated 13 Oct 2009
Article Copyright 2009 by Paulo Zemek
Everything else Copyright © CodeProject, 1999-2017
Layout: fixed | fluid