Search code examples
f#garbage-collectionweak-references

Why does the weak reference not get collected in this simple F# example?


open System

let WeakReferenceExample() =
    let mutable obj = new Object();
    let weak = new WeakReference(obj);

    GC.Collect();
    Console.WriteLine("IsAlive: {0}\nobj <> null is {1}\n---", weak.IsAlive, obj <> null);

    obj <- null;
    GC.Collect();
    Console.WriteLine("IsAlive: {0}", weak.IsAlive);

WeakReferenceExample()
Console.ReadKey()

Translated from the Rx In Action book sample. The above when run gives the following output which is different than what I get when I compile it in C# and run it.

IsAlive: True
obj <> null is True
---
IsAlive: True

Why is the weak reference not getting collected?


Solution

  • This is one of those cases where the measurement affects the result.

    The underlying cause is compiler (reverse?) optimizations for better locals during Debugging.

    When you write:

    let list1 = [1; 2; 3]
    let list2 = [3; 4; 5]
    printfn "%b" (list1 = list2)
    

    This elaborates every sub-expression to:

    let list1 = [1; 2; 3]
    let list2 = [3; 4; 5] 
    let list1' = list1
    let list2' = list2
    let is_eq = list1'.Equals(list2')
    printfn "%b" (is_eq)
    

    Now you can begin to guess where this is going.

     Console.WriteLine("IsAlive: {0}\nobj <> null is {1}\n---", weak.IsAlive, obj <> null);
    

    Elaborates to:

        let isAlive = weak.IsAlive
        let obj' = obj
        let isNotNull = obj' <> null
        Console.WriteLine("IsAlive: {0}\nobj <> null is {1}\n---", isAlive, isNotNull);
    

    Now when you do:

        obj <- null;
        GC.Collect();
    

    note that a reference still exists in obj'.

    So the object won't be collected during the GC pass. And that's what WeakReference is showing.

    So interestingly enough, if you comment out the first Console.Writeline, there's no sub-expression elaboration, hence no references, and you get:

    IsAlive: False
    

    Or you could just build it in Release mode.