3

I wrote a small Console app experiment:

static void Main()
{
    int GetTotalCollections()
    {
        var total = 0;

        for (var generation = 0; generation <= GC.MaxGeneration; generation++)
            total += GC.CollectionCount(generation);

        return total;
    }

    CreateGarbage();

    WriteLine("Number of collections occurred: {0}", GetTotalCollections());
}

static void CreateGarbage()
{
    WriteLine(new object().GetHashCode());
    WriteLine(new object().GetHashCode());
    WriteLine(new object().GetHashCode());
    ReadKey();
}

When I run it in Release mode without the debugger attached, I get 0 collections for all the generations.

As it seemed weird, I attached WinDbg to the process and !dumpheap -live does not show the objects whereas !dumpheap does. Unfortunately -live is not part of the official documentation for SOS, but I was assuming it showed only uncollected objects.

Is the GC collecting and not reporting it (as it should be in this scenario) or have I misunderstood the !dumpheap -live output? (maybe showing only object with a GC root?)

If I allocate more objects to be sure I trigger a collection (or invoke GC.Collect) I get a collection count increase, but still the same output in WinDbg:

static void GetValue()
{
    for (var i = 0; i < 1000000; i++)
        new object();

    ReadKey();
}

EDIT: The last sentence was an oversight (which caused the confusion), if I force the collection, !dumpheap -stat does not show the objects any more as expected (I just forgot to run the app from WinDbg before pressing a key to force the collection.)

!dumpheap -stat shows uncollected objects both live and dead and !dumpheap -stat -live shows only objects with a GC root as @Thomas answered. Once collected they disappear from the output of all the commands.

Stefano d'Antonio
  • 5,874
  • 3
  • 32
  • 45

2 Answers2

4

No, the GC isn't collecting and not reporting it, it just hasn't run any collections yet. Just because you've created a few objects that are eligible for collection doesn't mean that any collections have actually happened, just that when a collection happens those objects will get cleaned up by it. The GC will actually run whenever it thinks it needs to, based on a lot of heuristics.

Servy
  • 202,030
  • 26
  • 332
  • 449
  • (still assuming `!dumpheap -live` shows uncollected objects) how would you explain it missing those objects? GC "could" be happening as it can be triggered by an allocation. – Stefano d'Antonio Aug 16 '17 at 18:04
  • @Stefanod'Antonio The objects aren't alive. They're eligible for collection, meaning they're dead. They just haven't actually been collected yet. – Servy Aug 16 '17 at 18:12
3

Unfortunately -live is not part of the official documentation for SOS

When I type !help dumpheap, it says:

-live     Only print live objects
-dead     Only print dead objects (objects which will be collected in the
          next full GC)

Live objects are objects that are referenced by a garbage collection root and dead objects are objects that have no such reference any more and can be garbage collected whenever .NET thinks it's time to do that.

You should be able to find the difference when you run !gcroot on a live object and on a dead object. The live object should be connected to a root (pinned handle, thread, static variable, freachable queue, ...) but the dead object is not.

Example for a dead object:

0:007> !gcroot -all 029e3be4
Found 0 roots.

Example for a live object:

0:007> !gcroot -all 029e406c
HandleTable:
    00b913f0 (pinned handle)
    -> 039e24d0 System.Object[]
    -> 029e406c System.String

Found 1 roots.
Thomas Weller
  • 55,411
  • 20
  • 125
  • 222