Programmers Stack Exchange is a question and answer site for professional programmers interested in conceptual questions about software development. It's 100% free.

Sign up
Here's how it works:
  1. Anybody can ask a question
  2. Anybody can answer
  3. The best answers are voted up and rise to the top

I have not personally come across a situation where I've needed to use WeakReference type in .Net, but the popular belief seems to be that it should be used in caches. Dr Jon Harrop gave a very good case against the use of WeakReferences in caches in his answer to this question.

I've also often heard AS3 developers talk about using weak references to save on memory footprint but based on the conversations I've had it seems to add complexity without necessarily accomplishing the intended goal, and the runtime behaviour is rather unpredictable. So much so that many simply give up on it and instead manage the memory usage more carefully/optimize their code to be less memory intensive (or making the trade-off of more CPU-cycles and smaller memory footprint).

Dr Jon Harrop also pointed out in his answer that the .Net weak references are not soft, and there's an aggressive collection of weak references at gen0. According to MSDN, long weak references gives you the potential to recreate an object, but the state of the object remains unpredictable.!

Given these characteristics, I can't think of a situation where weak references would be useful, perhaps someone could enlighten me?

share|improve this question

migrated from stackoverflow.com Jan 31 '13 at 0:10

This question came from our site for professional and enthusiast programmers.

3  
You've already outlined potential uses for it. Of course there are other ways to approach these situations, but there's more than one way to skin a cat. If you're looking for a bullet-proof "you should always use a WeakReference when X", I doubt you'll find one. – itsme86 Jan 30 '13 at 23:52
2  
@itsme86 - I'm not looking for a bullet-proof use case, just ones that weak references are a good fit for and makes sense. The cache use case for instance, because weak references are so eagerly collected it's just going to cause more cache misses even when you have plenty of memory available – theburningmonk Jan 31 '13 at 0:04
4  
I'm a little disappointed this is getting some many votes to close. I wouldn't mind seeing an answer or some discussion about this (in b4 "Stack Overflow is not a forum"). – ta.speot.is Jan 31 '13 at 0:05
    
@theburningmonk That is the offset in return for memory gain. In today's framework, it is doubtful that anyone would reach straight for the WeakReference tool even when implementing a cache since there are comprehensive caching systems readily available. – itsme86 Jan 31 '13 at 0:06
    
Here is an embarrassingly over-complicated example of using them (for the Weak Event Pattern that ta.speot.is describes below) – Benjol Jan 31 '13 at 9:06

I've found legitimate practical applications of weak references in the following three real-world scenarios that actually happened to me personally:

Application 1: Event handlers

You're an entrepreneur. Your company sells a spark lines control for WPF. Sales are great but support costs are killing you. Too many customers are complaining of CPU hogging and memory leaks when they scroll through screens full of spark lines. The problem is their app is creating new spark lines as they come into view but data binding is preventing the old ones from being garbage collected. What do you do?

Introduce a weak reference between the data binding and your control so that data binding alone will no longer prevent your control from being garbage collected. Then add a finalizer to your control that tears down the data binding when it gets collected.

Application 2: Mutable graphs

You're the next John Carmack. You've invented an ingenius new graph-based representation of hierarchical subdivision surfaces that makes Tim Sweeney's games look like a Nintendo Wii. Obviously I'm not going to tell you exactly how it works but it all centers on this mutable graph where the neighbors of a vertex can be found in a Dictionary<Vertex, SortedSet<Vertex>>. The graph's topology keep changing as the player runs around. There's only one problem: your data structure is shedding unreachable subgraphs as it runs and you need to remove them or you'll leak memory. Luckily you're a genius so you know there is a class of algorithms specifically designed to locate and collect unreachable subgraphs: garbage collectors! You read Richard Jones' excellent monograph on the subject but it leaves you perplexed and concerned about your imminent deadline. What do you do?

Simply by replacing your Dictionary with a weak hash table you can piggyback the existing GC and have it automatically collect your unreachable subgraphs for you! Back to leafing through Ferrari adverts.

Application 3: Decorating trees

You're hanging from the ceiling of a cyclindrical room at a keyboard. You've got 60 seconds to sift through some BIG DATA before someone finds you. You came prepared with a beautiful stream-based parser that relies upon the GC to collect fragments of AST after they've been analyzed. But you realise you need extra metadata on each AST Node and you need it fast. What do you do?

You could use a Dictionary<Node, Metadata> to associate metadata with each node but, unless you clear it out, the strong references from the dictionary to old AST nodes will keep them alive and leak memory. The solution is a weak hash table which keeps only weak references to keys and garbage collects key-value bindings when the key becomes unreachable. Then, as AST nodes become unreachable they are garbage collected and their key-value binding is removed from the dictionary leaving the corresponding metadata unreachable so it too gets collected. Then all you have to do after your main loop has terminated is slide up back through the air vent remembering to replace it just as the security guard comes in.

Note that in all three of these real-world applications that actually happened to me I wanted the GC to collect as aggressively as possible. That's why these are legitimate applications. Everybody else is wrong.

share|improve this answer
2  
Weak references won't work for application 2 if the unreachable subgraphs contain cycles. This is because a weak hash table usually has weak references to the keys, but strong references to the values. You'd need a hash table that maintains strong references to the values only while the key is still reachable --> see ephemerons (ConditionalWeakTable in .NET). – Daniel Aug 14 '13 at 14:47
    
@Daniel Isn’t the GC supposed to be able to handle unreachable cycles? How would this not be collected when an unreachable cycle of strong references would be collected? – binki Jan 8 at 5:59
    
Oh, I think I see. I just assumed that ConditionalWeakTable is what applications 2 and 3 would use whereas some people in other posts actually use Dictionary<WeakReference, T>. No idea why—you’d always end up with a ton of null WeakReferences with values that cannot be accessed by any key regardless of how you do it. Ridik. – binki Jan 8 at 6:08

Given these characteristics, I can't think of a situation where weak references would be useful, perhaps someone could enlighten me?

Microsoft document Weak Event Patterns.

In applications, it is possible that handlers that are attached to event sources will not be destroyed in coordination with the listener object that attached the handler to the source. This situation can lead to memory leaks. Windows Presentation Foundation (WPF) introduces a design pattern that can be used to address this issue, by providing a dedicated manager class for particular events and implementing an interface on listeners for that event. This design pattern is known as the weak event pattern.

...

The weak event pattern is designed to solve this memory leak problem. The weak event pattern can be used whenever a listener needs to register for an event, but the listener does not explicitly know when to unregister. The weak event pattern can also be used whenever the object lifetime of the source exceeds the useful object lifetime of the listener. (In this case, useful is determined by you.) The weak event pattern allows the listener to register for and receive the event without affecting the object lifetime characteristics of the listener in any way. In effect, the implied reference from the source does not determine whether the listener is eligible for garbage collection. The reference is a weak reference, thus the naming of the weak event pattern and the related APIs. The listener can be garbage collected or otherwise destroyed, and the source can continue without retaining noncollectible handler references to a now destroyed object.

share|improve this answer
    
That URL auto selects the newest .NET version (4.5 currently) in which 'this topic is no longer available'. Selecting .NET 4.0 instead works (msdn.microsoft.com/en-us/library/aa970850(v=vs.100).aspx) – maxp Oct 1 '15 at 9:03
1  
Thanks @maxp, I've updated the answer. – ta.speot.is Oct 1 '15 at 10:22

Let me put this out first and come back to it:

A WeakReference is useful when you want to keep tabs on an object, but you DO NOT want your observations to prevent that object from being collected

So let's start from the beginning:

--apologies in advance for any unintentional offense, but I'm gonna back up to "Dick and Jane" level for a moment since one can never tell ones audience.

So when you've got an object X - let's specify it as an instance of class Foo - it CANNOT live on it's own (mostly true); In the same way that "No man is an island", there are only a few ways that an object can promoted to Islandhood - although it's called being a GC root in CLR speak. Being a GC Root, or having an established chain of connections/references to a GC root, is basically what determines whether or not Foo x = new Foo() gets garbage collected.

If you cannot walk your way back to some GC root either by heap or stack walking, you are effectively orphaned, and will likely be marked/collected next cycle.

At this point, let's look at some horrible-contrived examples:

First, our Foo:

public class Foo 
{
    private static volatile int _ref = 0;
    public event EventHandler FooEvent;
    public Foo()
    {
        _ref++;
        Console.WriteLine("I am #{0}", _ref);
    }
    ~Foo()
    {
        Console.WriteLine("#{0} dying!", _ref--);
    }
}

Fairly simple - it's not thread safe, so don't try that, but keeps a rough "reference count" of active instances and decrements when they are finalized.

Now let's look at a FooConsumer:

public class NastySingleton
{
    // Static member status is one way to "get promoted" to a GC root...
    private static NastySingleton _instance = new NastySingleton();
    public static NastySingleton Instance { get { return _instance;} }

    // testing out "Hard references"
    private Dictionary<Foo, int> _counter = new Dictionary<Foo,int>();
    // testing out "Weak references"
    private Dictionary<WeakReference, int> _weakCounter = new Dictionary<WeakReference,int>();

    // Creates a strong link to Foo instance
    public void ListenToThisFoo(Foo foo)
    {
        _counter[foo] = 0;
        foo.FooEvent += (o, e) => _counter[foo]++;
    }

    // Creates a weak link to Foo instance
    public void ListenToThisFooWeakly(Foo foo)
    {
        WeakReference fooRef = new WeakReference(foo);
        _weakCounter[fooRef] = 0;
        foo.FooEvent += (o, e) => _weakCounter[fooRef]++;
    }

    private void HandleEvent(object sender, EventArgs args, Foo originalfoo)
    {
        Console.WriteLine("Derp");
    }
}

So we've got an object that's already a GC root of it's own (well...to be specific, it'll be rooted via a chain straight to the app domain running this application, but that's another topic) that has two methods of latching on to a Foo instance - let's test it out:

// Our foo
var f = new Foo();

// Create a "hard reference"
NastySingleton.Instance.ListenToThisFoo(f);

// Ok, we're done with this foo
f = null;

// Force collection of all orphaned objects
GC.Collect();
GC.WaitForPendingFinalizers();
GC.Collect();

Now, from the above, would you expect the object-that-was-once-referred-to by f to be "collectable"?

No, because there is another object now holding a reference to it - the Dictionary in that Singleton static instance.

Ok, let's try the weak approach:

f = new Foo();
NastySingleton.Instance.ListenToThisFooWeakly(f);

// Ok, we're done with this foo
f = null;

// Force collection of all orphaned objects
// This should collect # 2 - you'll see a "#2 dying"
GC.Collect();
GC.WaitForPendingFinalizers();
GC.Collect();

Now, when we whack our reference to the-Foo-that-was-once-f, there are no more "hard" references to the object, so it is collectable - the WeakReference created by the weak listener won't prevent that.

Good use cases:

  • Event handlers (Although read this first: Weak Events in C#)

  • You've got a situation where you would cause a "recursive reference" (i.e., object A refers to object B, which refers to object A, also referred to as a "Memory Leak") (edit: derp, of course this isn't true)

  • You want to "broadcast" something to a collection of objects, but you don't want to be the thing keeping them alive; a List<WeakReference> can be maintained easily, and even pruned by removing where ref.Target == null

share|improve this answer
1  
Regarding your second use case, the garbage collector handles circular references just fine. "object A refers to object B, which refers to object A" is definitely not a memory leak. – Joe Daley Jan 31 '13 at 0:59
    
@JoeDaley I agree. The .NET GC uses a mark and sweep algorithm which (I believe I'm recalling this correctly) marks all objects for collection and then follows references from "roots" (references of objects on the stack, static objects), unmarking objects for collection. If a circular reference exists but none of the objects are accessible from a root, the objects are not unmarked for collection and are thus eligible for collection. – ta.speot.is Jan 31 '13 at 1:08
1  
@JoeDaley - You are both, of course, correct - was rushing it there towards the end...I'll edit that out. – JerKimball Jan 31 '13 at 2:02

Weak references are useful when objects don't own their referenced resources and, unlike soft references, you really want memory to be freed as soon as possible -- simple as that. Not every entity in the codebase which has a reference to a resource should be a shared owner of that resource.

Let's say you have a 3D animation software. Users either create or load in big polygonal meshes that span anywhere from hundreds of bytes to gigabytes. Let's imagine the gigabytes case.

The software also has cameras that let you see these meshes through a viewport. The cameras contain a list of meshes that can be excluded from the camera view (making them invisible to that camera). The camera isn't an owner of a mesh (the scene is), it just wants to store a list of existing meshes to exclude from rendering.

In this case, the camera exclusion list can store weak references for meshes to exclude. This way, when the user removes the mesh from the scene, the gigabytes of memory can actually be freed, and soon after, even if we forgot to remove the mesh weak reference from the camera exclusion list.

Now, ideally we always remember to remove the mesh reference from this camera exclusion list through an event or something of the sort, whether or not it's a weak reference. But the weak reference guarantees that we won't silently leak gigabytes of memory over and over until the software is shut down even if we forget.

When iterating through the camera exclusion list to figure out what to exclude from rendering, we can then check through the weak references to see if their "referees" have been collected. You can then treat that as an error if you like and assert or throw or log something. Now you know quickly, and fairly deterministically, that you forgot to remove the mesh from the camera list when the user requests to remove/unload the mesh from the scene. With a strong reference, you'd never know until you noticed that your software was mysteriously leaking gigabytes of memory and tried to carefully hunt down where it was happening (this can be a real nightmare even with leak detectors).

So that's where weak references can really come in handy, since they don't prevent the resource from being freed (collected) whatsoever. They're really useful in programs where leaking is really bad. Using strong references everywhere is okay for programs where leaking isn't a big deal, but when we're working with massive data being potentially referenced in various places of a complex codebase, weak references can be your best friend against memory leaks which can be really hard to avoid with garbage collection in such complex codebases otherwise.

share|improve this answer

A very illustrative example of weak references used to good effect is the ConditionalWeakTable, which is used by the DLR (among other places) to attach additional "members" to objects.

You don't want the table to keep the object alive. This concept simply could not work without weak references.

But it kind of seems to me that all the uses for weak references came long after they were added to the language, since weak references have been part of .NET since version 1.1. It just seems like something you'd want to add, so that the lack of deterministic destruction won't back you into a corner as far as language features are concerned.

share|improve this answer
    
I've actually discovered that although the table uses the concept of weak references, the actual implementation does not involve the WeakReference type, as the situation is a lot more complex. It uses different functionality exposed by the CLR. – GregRos Jan 11 at 20:03

If you have cache layer implemented with C# it's much better too put your data in cache as weak references, it could help to improve your cache layer performance.

Think that approach also could be applied to session implementation. Because session is long living object most of the time, it could be some case when you have no memory for new user. In that case it will be much better to delete some else user session object then throwing OutOfMemoryException.

Also, if you have a large object in your application (some big lookup table, etc), that should be used quite seldom and recreating of such an object isn't a very expensive procedure. Then better have it like a week reference to have a way to free your memory when you really need that.

share|improve this answer
5  
but the problem with weak references (see the answer I referenced) is that they're very eagerly collected, and the collection is not linked to the availability of memory space. So you end up with more cache misses when there's no pressure on memory. – theburningmonk Jan 31 '13 at 0:07
1  
But for your second point about large objects, the MSDN doc states that whilst long weak references allow you to recreate an object, it's state remain unpredictable. If you're going to recreate it from scratch each time, why bother using a weak reference when you can just call a function/method to create it on demand and return a transient instance? – theburningmonk Jan 31 '13 at 0:09
    
There's one situation where caching is helpful: If one will be frequently creating immutable objects, many of which will happen to be identical (e.g. reading many lines from a file that is expected to have many duplicates) each string will be created as a new object, but if a line matches another line to which a reference already exists, memory efficiency may be improved if the new instance is abandoned and a reference to the pre-existing instance is substituted. Note that this substitution is useful because the other reference is being held anyway. If it weren't code should keep the new one. – supercat Feb 27 '13 at 22:51

protected by gnat Jun 25 '15 at 12:54

Thank you for your interest in this question. Because it has attracted low-quality or spam answers that had to be removed, posting an answer now requires 10 reputation on this site (the association bonus does not count).

Would you like to answer one of these unanswered questions instead?

Not the answer you're looking for? Browse other questions tagged or ask your own question.