Hi all,
I have a .NET 4.5.2 app that is supposed to run continuously for months. The things it does are not particularly memory- or CPU-intensive, but it does create and destroy many objects, some of which may be large. It does not use any unmanaged resources.
I observe that over time the app's RAM usage, as reported by Task Manager, tends to grow to a very large factor of what it should be. I've used PerfView to get a heap dump, and it tells me that 99% of it is "Unreachable Memory".
I understand that this is memory that is unreferenced, but that the GC has not reclaimed yet. My question is: why does the GC let 99% of the memory allocated to my application remain unclaimed, even after days or weeks? I've read several articles on the GC, but I still can't figure this out.
From searching around, I've found two broad categories of answers.
1) Trust the GC to release that memory if and when the system requires it. This makes me nervous. Even assuming that I can trust the GC to release memory before other things start throwing OOM exceptions, surely there should be a way to tell it that it's OK to run an extremely aggressive GC operation right now, and I don't care if it takes in the order of seconds, just free everything and compact everything until all I have allocated are live objects nicely stacked one after the other? For example, for diagnostic purposes, or because I know that I'm going to need that RAM quickly in a minute or so, or just because I'm a control freak? I have tried GC.Compact with every possible flag, as well as GCSettings.LargeObjectHeapCompactionMode, but nothing I do seems to even put a dent in that 99% Unreachable Memory.
2) Redesign your application so that it's more GC-friendly; reuse large objects, and so on. Again, this makes me nervous. I get that if you want performance, you need to take control of things. But this app is very much not a performance app. It does not deal with massive objects. It does not deal with millions of objects. It does not require millisecond response times. If the GC can't handle that, surely that's a major failure on its part? I mean, I get that my app is GC-unfriendly, but I would expect this to result in maybe the app taking two or three times as much RAM as it needs, or maybe freezing for a second every now and then. Worst case, maybe have to tell it once per day to do an exceptionally aggressive GC. I wouldn't expect it to expand to fill all available system RAM forever with no way to release it.
Now, what I hope is, that there is something else I'm doing wrong, and that once I find it and fix it the problem will be gone and the app will go back to using the 50 megs or so it actually needs at any given time. But I'm having a lot of problems investigating this, because nearly every search result I find falls into one of the two categories above.
Also, I'm really confused by the fact that nothing I do with GC.Collect() seems to have any effect. I would think I have a good old memory leak, except that I don't use unmanaged resources and PerfView tells me that all of the memory is Unreachable. So why is there no way to GC it?
Any ideas?