Bizarre GC behavior (while testing a WeakDictionary)

I am encountering a bizarre GC behavior while testing a dictionary that I have designed.

The designed dictionary is a WeakDictionary. That is basically a Dictionary where all values are kept through a WeakReference. The WeakDictionary does not prevent the values from being collected if they are not rooted elsewhere.

Here below is the NUnit code that I am using to test the WeakDictionary. As you can see there are lines with REQUIRED. If those lines are omitted then the last assertion fails at index itemCount - 1. Exactly like if the internal loop reference where holding a reference to the object outside there loop scope.

If I am correct (not a bug in my code), I found this behavior pretty bad and pretty counter intuitive. A reference should not continue to exist outside its scope.

Did I miss something
Joannes


Dictionary<int, object> dictionary = new Dictionary<int,object>();
WeakDictionary<int, object> weakDictionary = new WeakDictionary<int,object>();

// testing WeakDictionary.Add
for (int i = 0; i < itemCount; i++)
{
    object obj = new object();
    dictionary.Add(i, obj);
    weakDictionary.Add(i, obj);

    Assert.AreEqual(dictionary.Count, weakDictionary.Count, "#A00 Unexpected Count.");

    obj = null; // REQUIRED BUT WHY
}

// testing WeakDictionary.get_Item
for (int i = 0; i < itemCount; i++)
{
    object obj1 = dictionaryIdea;
    object obj2 = weakDictionaryIdea;

    Assert.IsTrue(object.ReferenceEquals(obj1, obj2), "#A01 Unexpected value.");

    obj1 = null; // REQUIRED BUT WHY
    obj2 = null; // REQUIRED BUT WHY
}

dictionary = null;
GC.Collect(); // emptying the WeakDictionary

// testing the weakness of WeakDictionary
for (int i = 0; i < itemCount; i++)
{
    object obj = weakDictionaryIdea;
    Assert.IsNull(obj, "#A02 Null value expected at index " + i + ".");
}

 



Answer this question

Bizarre GC behavior (while testing a WeakDictionary)

  • codepro2

    Well I never would have guessed that - are there any good references in one place on all the subtle differences between debug and release modes

    I found the following that describe this effect in a bit more detail:
    http://discuss.develop.com/archives/wa.exe S2=dotnet&L=DOTNET&q=&s=WeakReference%20and%20optimization&f=&a=&b=




  • ags2886

    Hi Joannes

    Are you compiling in debug mode   If so, the JIT will extend the lifetimes of objects to the end of the method.  Try rebuilding in release mode, and you shouldn't see the assert.

     

    -Chris


  • Ushanta

    The WeakDictionary does not have a finalizer.
    If the first loop null assignement is commented-out the final assertion fails.
    In the second loop, the objects are alive (because they are held into a "normal" dictionary).

    Joannes

  • Blane Nelson

    Hi taumon

    I've written a blog entry about this issue:

    http://blogs.msdn.com/clyon/archive/2004/12/01/273144.aspx

     

    -Chris


  • JasonJ17

    Does your WeakDictionary class have a finalizer If so, maybe the finalization thread is still running by the time you reach the final assert

    You could try
    GC.Collect();
    GC.WaitForPendingFinalizers();
    GC.Collect();

    P.S. I think you should remove the null assignment in the first for loop - as it is, you assign the object in the Dictionary and WeakDictionary to null, so the assertion in the second loop just always compares null to null.


  • Raven.Liu

    The code is available under LGPL on sourceforge.net.

    Joannes

  • Amol Puri

    Oops, you're right I was having a dumb moment.

    Can you post the code for the WeakDictionary


  • Bizarre GC behavior (while testing a WeakDictionary)