How can I prevent a full gc on memory snapshots?
jbeerhalter2
Posts: 4
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.
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
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 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.
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?