Search code examples
c#.netreferencegarbage-collectionweak-references

Why is there still a reference onto this string?


I was tyoing around with WeakReference and WeakReference<T>. They only work with classes (obviously, reference) so I did an example with a string (string is a class in .Net).

When I ran the following code snippet, it didn't provide the result I expected, in the meaning that the WeakReference still contained the string.

  string please = "wat";
  WeakReference<string> test = new WeakReference<string>(please);
  string testresult;
  please = null;
  GC.Collect();
  bool worked = test.TryGetTarget(out testresult);
  Console.WriteLine("it is " + worked);

result: "it is true"

Now when I created a simple wrapper class around the string:

class TestWeakStuff
{
  public string Test { get; set; }
}

and used it instead of the string, it did return my expected result:

  TestWeakStuff testclass = new TestWeakStuff() { Test = "wat" };
  WeakReference<TestWeakStuff> test2 = new WeakReference<TestWeakStuff>(testclass);
  TestWeakStuff testresult2;
  testclass = null;
  GC.Collect();
  bool worked2 = test2.TryGetTarget(out testresult2);
  Console.WriteLine("2nd time is " + worked2);

Result: "2nd time is false"

I tried the same with the non generic WeakReference class, and the result is the same.

Why is the String not being claimed by the garbage collector?

(GC.Collect() does claim all generations, external GC call is with -1 (all generations))


Solution

  • String literals are not a good candidate to test GC behavior. String literals are added to the intern pool on the CLR. This causes only one object for each distinct string literal to live in memory. This is an optimization. Strings in the intern pool are referenced forever and never collected.

    Strings are not an ordinary class. They are intrinsics to the runtime.

    You should be able to test it with new string('x', 10) which creates a new object each time. This is guaranteed to be so. Sometimes, this is being used to use unsafe code to write to strings before publishing them to other code. Can be using with native code as well.

    It's probably best to drop testing strings entirely. The results you obtain are not particularly interesting or guaranteed to remain stable across runtime changes.

    You could test with new object() which would be the simplest way to test it.