How can I prevent a full gc on memory snapshots?

jbeerhalter2jbeerhalter2 Posts: 4
edited January 12, 2016 4:14PM in ANTS Memory Profiler 8
My application has a nasty habit of creating a bunch of objects, letting them get to gen 2, and then on a full GC they all get cleaned up. Trouble is, we don't know what the objects are or where they're coming from. If we run the memory profiler we see the classic saw toothed graph, but since snapshots cause a full GC, we can't get any visibility into what kinds of objects are being created. How does one answer this question? How can one look at the growth of gen 2?

In production these collections are taking 5-6 seconds and causing usability issues with our application, so it's imperative that we address them.

Comments

  • Hi there, I'm so sorry to say that there isn't a way to stop the profiler from forcing a full gc on the memory snapshots (the developers have chosen this behavior so that the results focus on objects that can't be collected by a GC).

    Regarding this memory issue you're seeing though, do you by any chance call on GC.Collect manually from your code? One possible reason for this behavior is if a manual call is forcing Gen 1 objects into Gen 2 prematurely.

    If not though, can I just confirm-- if you enable the "Gen 2 heap size" counter and then take a snapshot to force a GC as soon as the previous GC promotes the bunch of objects to Gen2, do they immediately get get picked up by the GC or do they actually stay in memory for a bit longer? And do you see large spikes/drops for the Gen 2 heap counter in the timeline?

    Thanks!

    Jessica Ramos | Product Support Engineer | Redgate Software

    Have you visited our Help Center?


  • I don't manually call GC anywhere in our code. We do have a hook to do that, but only for memory debugging purposes.

    I do see large spikes in the Gen 2 Heap Counter

    The objects tend to stay in memory a bit longer, but not much. Many of them are replaced every couple of seconds.
  • Ah okay, thanks very much for those details!

    As I think about this more, if only a gen1 GC is getting triggered, that means that gen1 is reaching its threshold. .NET changes this threshold constantly, but I'm wondering if at the point in your application right before the gen1 GC happens, if the gen1 heap is usually around the same size?

    If there is an expected threshold, you can then take a snapshot when the gen1 heap nears that size, before the gen1 GC actually gets triggered. Then it may be useful to see which objects were promoted to gen2 (by the full GC) to see why they are still in memory and perhaps if the gen1 heap is unnecessarily getting filled up. (And if this is possible, you can then take a second snapshot to clean up gen2 and see which classes decreased in size --that may also help narrow down the issue to a particular class.)

    Can I also just check if you may be using a lot of finalizable objects? That could mean that objects are still promoted to the next generation even though they are on they are no longer needed (and then the next GC or two usually cleans them up). When you take snapshots with the profiler, (although most of the gen2 objects are getting cleaned up), do you generally see a lot of objects in the class list if you enable the "kept in memory exclusively by GC Root of type finalizer queue" filter? Or if you add a customer performance counter to the timeline to monitor "Promoted Finalization-Memory from Gen 0" (I believe this actually includes memory promoted from both gen0 and gen1), is this usually quite high after a garbage collection?

    Jessica Ramos | Product Support Engineer | Redgate Software

    Have you visited our Help Center?


Sign In or Register to comment.