0

I'm currently investigating a memory leak, which I can't really explain and now I'm searching for helpful links or ideas.

Here is a screenshot of the native memory of the application (made with the .NET Memory profiler):

enter image description here

The application takes around 2.2 GB (which is normal). The dump was taken when the application had around ~3.5 GB. And these Gaps in the generation #0 are what I currently can't explain. For me this seems as the garbage collector doesn't compact the gaps in generation #0.

In order to have one clear question:

  • How do these kind of gaps happen? For me this seems as the GC has collected dead objects but hasn't compacted the heap. I know that I can't trigger or force the GC to compact the heap.

I've searched on this site for similar questions, but most of these are about the LOH, (which seems fine in my case). The only question which has some kind of similar large gaps is this: What are GC Holes, but I can't see how 2 KB of Generation #0 pinned instances produce 1 GB holes. Another question is about the threshold to trigger the GC GC thresholds. But I can't believe that there wasn't a single compaction phase.

Vulcano
  • 415
  • 10
  • 25
  • I would start from looking from another perspective - record session from PerfView (GC Collect Only option will be fine) and look at GCStats report - GC Events by Time table will list all GCs with many detailed information (including fragmentation of each generation, GC reasons and so on, and so forth). Such dynamic view of the problem may be helpful here. – Konrad Kokosa May 02 '18 at 09:36
  • Thanks I'll try this. But it might take some time, as these dumps take weeks to show the leak that large. – Vulcano May 02 '18 at 10:22
  • @Vulcano I'm also facing similar situation. Would you please explain what it is and why it is? How can we resolve it. I tried to search on .net memory profiler site as well but didn't get much details. Please help. Thanks – Vikas Jan 16 '20 at 05:50
  • @Vikas I wrote a small answer what we did. Good luck. – Vulcano Jan 16 '20 at 06:34

2 Answers2

1

Holes/Gaps represent memory that is unused between two allocated instances. “Holes” appear when the heap is not fully compacted (due to pinned instances or optimizations in the garbage collector).

You can't explicitly compact the heap per se. However, the GC is sometimes able to do this performing either a full or partial collect.

So you have gaps, what does it mean? It means it makes more complicated to allocate to the heap, what can you do about it... not a lot. if its a performance issue, you could play around with the GC a bit. However if you are using lots of pinned memory there isn't much you can do. This is called fragmentation

Additional resources

Garbage Collector Basics and Performance Hints

What are GC holes?

GC behavior when pinning an object

What causes memory fragmentation in .NET

No More Memory Fragmentation on the .NET Large Object Heap

TheGeneral
  • 79,002
  • 9
  • 103
  • 141
  • I don't know about this, but then it seems that 2KB of objects produce >1GB of gaps? Or is the memory profiler assignment incorrect? Most of the other links are about the LOH which seem to be fine in my case. – Vulcano May 02 '18 at 10:34
  • What sort of work are you doing and why are you worried, do you have a leak, holes arnt leaks btw – TheGeneral May 02 '18 at 10:45
  • 1
    I'm hunting a rare bug. The reports I get are about the application getting slow and taking up too many resources after the application ran for several weeks. Here is another screenshot of another dump: https://imgur.com/a/fljFyHH there the LOH overhead is considerable larger, but the 4GB of gaps in Gen#0 is what I'm concerned about... – Vulcano May 02 '18 at 11:15
  • @Vulcano before you do a dump it might worth while doing a gc.collect and wait for pending finalizers – TheGeneral May 02 '18 at 11:29
  • Thanks I'll try that. I just wanted to know before I started to do "stuff" to kinda know what I'm doing. And although I read now quite a bit about GC, forced collection, pinned instances and LOH, I'm still not sure what exactly I'm seeing. – Vulcano May 02 '18 at 11:58
0

To this day I'm still not certain what exactly caused this issue but it was resolved this way. First of all I had some Windows Dumps and NMP Dumps and poked a lot in them (WinDBG was pretty helpful). There I found out, that some of the data was related to sockets.

So the only shot was to rewrite the current socket implementation from the Begin* calls to the *Async calls. (see this question)

There we followed the msdn implementation. (Create one large buffer and reuse it). Especially the comments there point in that direction:

    // Allocates one large byte buffer which all I/O operations use a piece of.  This gaurds 
    // against memory fragmentation

The gaps are not gone now, but they are much smaller and they don't grow any more...

Vulcano
  • 415
  • 10
  • 25