2

On inspecting a crash dump file for an out of memory exception reported by a client the results of !DumpHeap -stat showed that 575MB of memory is being taken up by 45,000 objects of type "Free" most of which I assume would have to reside in Gen 2 due to the size.

The first places I looked for problems were the large object heap (LOH) and pinned objects. The large object heap with free space included was only 70MB so that wasn't the issue and running !gchandles showed:

GC Handle Statistics:
Strong Handles:        155
Pinned Handles:        265
Async Pinned Handles:  8
Ref Count Handles:     163
Weak Long Handles:     0
Weak Short Handles:    0
Other Handles:         0

which is a very small number of handles (around 600) compared to the number of free objects (45,000). To me this rules out the free blocks being caused by pinning.

I also looked into the free blocks themselves to see if maybe they had a consistent size, but on inspection the sizes varied widely and went from just short of 5MB to only around 12 bytes or so.

Any help would be appreciated! I am at a loss since there is fragmentation but no signs of it being cause by the two places that I know to look which are the large object heap (LOH) and pinned handles.

Billy ONeal
  • 97,781
  • 45
  • 291
  • 525
Chiune Sugihara
  • 1,004
  • 1
  • 7
  • 12
  • Do an !address –summary to get an overview of you process, pay attention to the heap usage (which are the native heaps), in case you have a native leak. Updater your post with the results if you need more help. – Kjell Gunnar Apr 03 '15 at 16:42
  • @KjellGunnar - The output of !address -summary shows that only 146MB is used for native heaps. The majority is in the section which should be mostly CLR related items and the majority of this majority is from the 575MB of free objects. – Chiune Sugihara Apr 03 '15 at 17:08
  • 265 is actually quite a few pinned handles. Fragmentation is cumulative, so over many GCs with ~265 pinned handles, it will add up. – Steve Johnson Apr 03 '15 at 19:59
  • @SteveJohnson - Are you sure that this is true about the fragmentation of pinned handles being cumulative. I was reading [OutOfMemoryException and Pinning](http://blogs.msdn.com/b/yunjin/archive/2004/01/27/63642.aspx) and it was saying that Gen 0, 1, and 2 should be compacting when possible so wouldn't this eliminate the possibility of a cumulative fragmentation affect from pinned handles? – Chiune Sugihara Apr 06 '15 at 19:59

1 Answers1

1

In which generation are the free objects?

I assume would have to reside in Gen 2 due to the size

Size is not related to generations. To find out in which generation the free blocks reside, you can follow these steps:

From !dumpheap -stat -type Free get the method table:

0:003> !dumpheap -stat -type Free
total 7 objects
Statistics:
      MT    Count    TotalSize Class Name
00723538        7          100      Free
Total 7 objects

From !eeheap -gc, get the start addresses of the generations.

0:003> !eeheap -gc
Number of GC Heaps: 1
generation 0 starts at 0x026a1018
generation 1 starts at 0x026a100c
generation 2 starts at 0x026a1000
ephemeral segment allocation context: none
 segment    begin allocated     size
026a0000 026a1000  02731ff4 0x00090ff4(593908)
Large object heap starts at 0x036a1000
 segment    begin allocated     size
036a0000 036a1000  036a3250 0x00002250(8784)
Total Size   0x93244(602692)
------------------------------
GC Heap Size   0x93244(602692)

Then dump the free objects only of a specific generation by passing the start and end address (e.g. of generation 2 here):

0:003> !dumpheap -mt 00723538 0x026a1000 0x026a100c
 Address       MT     Size
026a1000 00723538       12 Free
026a100c 00723538       12 Free
total 2 objects
Statistics:
      MT    Count    TotalSize Class Name
00723538        2           24      Free
Total 2 objects

So in my simple case, there are 2 free objects in generation 2.

Pinned handles

600 pinned objects should not cause 45.000 free memory blocks. Still from my experience, 600 pinned handles are a lot. But first, check in which generation the free memory blocks reside.

Thomas Weller
  • 43,638
  • 16
  • 101
  • 185
  • I was mentioning that they were all Gen 2 due to the nearly trivial sizes of Gen 0 and 1 in comparison to the 100's of MBs of Free objects. I ran the command that you requested and basically 100% of the Free objects are Gen 2. Per the pinned handles most of them are System.Drawing.Internal.GPStream which seems like a stream leak, but even given that I don't know how a few hundred of these could create such a massive amount of free space. – Chiune Sugihara Apr 06 '15 at 19:56
  • 1
    @ChiuneSugihara: That would mean that with the next Gen2 garbage collection, the heap would be compacted again and potentially a lot of memory could be reused, right? Unfortunately you got a OOM exception before that time. Is there any place in your application where you know that a lot of memory is freed and you could call GC.Collect() in your code? Which .NET version does your application use? – Thomas Weller Apr 06 '15 at 20:13
  • Unfortunately the application is much too large for me to narrow down a specific area where GC.Collect could be called. I know in theory you shouldn't need to call it but have heard rumors about the GC not working completely correctly and manual calls to GC.Collect saving an application from crashing (not sure if it is true or not). The .NET version that we are using is 4.0. – Chiune Sugihara Apr 21 '15 at 15:55
  • @ChiuneSugihara: In .NET 2.0 I had a case where .NET preferred a CPU intense task over a garbage collection although memory was very low already. That resulted in a `OutOfMemoryException`. Calling `GC.Collect()` in a well-known place before the CPU intense task started solved the problem. It should remain a last resort, though. Remember that calling `GC.Collect()` increases the generation of all objects by 1, so calling it often will make a lot of objects part of Gen2, which is seldom collected. – Thomas Weller Apr 21 '15 at 18:56