Typically you don't need to dispose/release it manually - GC will take care of that for you. This is one of the many benefits of having a GC. The issue the OP is observing is actually related to the pace at which the GC frees the COM objects. Typically, if you do a lot of winRT object allocations in C#, the GC will take its time to free up memory, usually resulting in an OutOfMemory exception at some point.



I'm no GC expert but I'll try my best to clarify. Usually, GC gets triggered for one of the following reasons:

1. There isn't enough generation 0 threshold to satisfy the object allocation request (we call this "exceeding gen 0 budget"). A variation of this is we also have a "budget" for the large object heap and you'll get a GC too if that is exceeded.

2. System.GC.Collect is called (we call this "induced GC")

3. System memory runs low

If you keep allocating WinRT objects, you'll regularly get GC during allocation (#1). As long as those objects are no longer referenced and not promoted to older generations, they'll get cleaned up in this GC. So you'll see memory gets allocated and keep growing, and then come back to normal after a GC, in a fairly well behaved program. Things gets more complicated with real world applications where objects survive GC and gets promoted to older generations, and they might get collected a bit later, but eventually come back to normal after a full Gen 2 GC. If managed heap size keeps growing, that usually means either the program is keeping allocating a ton of objects and holding onto them (a leak, or simply too memory-hungry), or the finalizer thread for some reason couldn't keep up. Eventually GC will get a low system memory notification and will start more aggressively collecting memory, but that may or may not work depending on many factors (pinning, memory fragmentation, etc). If you end up getting a OutOfMemoryException, it might be (off the top of my head) one of the following reasons:

1. The program simply is holding onto too much memory

2. There is a managed leak - the program is unintentionally holding onto too much memory/objects without letting go

3. There is a native leak - somewhere native code is leaking. This is usually interop related (using C# interop features incorrectly, or C++ code has leak), but is most likely an app bug and not C# interop bug (not saying C# interop is bug-free - as much as I'd like to declare that, we'll know it's far from the truth. It's just that more often than not, C# interop is used incorrectly than an actual interop bug).

4. There is too much memory fragmentation - there is enough memory if you add them all, but there is simply not enough continuous space. A possible cause of fragmentation is excessive pinning.

5. GC tries to free memory, but finalizer thread simply couldn't keep up. With C# RCWs, it might be possible if the finalizer thread calling release trying to go back to another COM apartment (to service the release call), and getting stuck (or not as efficient as it could be). In WinRT, there are much less apartment-aware objects, so this is usually less of a problem in UWP.

6. Not really OutOfMemory per-se, but you do ran out of other unmanaged resources, like handles.

As you can see, understanding the behavior of memory allocation in managed programs is rather involved and is not as straight-forward as C/C++ (in the sense that memory consumption behavior is more difficult to predict since it is managed by GC, but with C++ it can also get rather complicated with memory pools). What usually works for me is to simply observe the memory growth pattern (in perfmon, for example) - if it keeps growing without ever coming back to a stable state, you might have a managed leak somewhere. Introducing a managed leak is actually very easy - imagine if you have a list of Pages saved in a static, and you want to keep a list of them so that you can consult some information from previous pages, and you keep adding to it, now you have a managed leak. A variation of this is to have a link list of them, and the entire list is kept alive by the current page. A profiler is usually your best friend to diagnosing such issues.

Now coming back to the pace issue - we do have a concept called memory pressure that basically tells GC how much native memory that native object holds, so that GC can make a better decision regarding when to collect memory. This is mostly intended to reduce the private working set and be a nice citizen (and don't starve the other apps). The hard part is calculating the exact cost of an native object. This can get really tricky if that object is holding another graph of objects, so typically only XAML objects will send CLR that information. Of course, an app can call AddMemoryPressure to communicate that to GC manually. Anyway, this is usually not why you get a OutOfMemoryException.

If there is a specific repro that demonstrate issues, I'll be happy to take a look. I don't think there is one from this thread (one is related to circular references, and the other seem to be proven not repro). Sorry if I somehow missed one.

Thanks,

Yi Zhang