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?
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.