The following forum(s) have migrated to Microsoft Q&A (Preview): Developing Universal Windows apps!
Visit Microsoft Q&A (Preview) to post new questions.

Learn More

 locked
[UWP] [Platform issue] Memory leaks in UWP C# applications RRS feed

  • General discussion

  • Looks like native memory in UWP is leaking like a hell.

    I have some memory issues in my app but when I checked it with Visual Studio profiling tools in regular "Managed" mode it shows there is no memory leaks in managed garbage collected memory and managed memory is used merely up to 5Mb. But app itself (even in .NET Native release mode) is using 160-180-200... Mb memory and is rising after usage. It looks mysterious as managed memory profiler detects no leaks at all and all unneeded managed objects are collecting nicely.

    After I have profiled app in "Managed+Native Mixed" mode, picture turned to be more grim. Windows Runtime native memory is leaking like a hell! Somebody can say "it's reference counted after all, you are using Windows Runtime wrong..." But stop! I'm C# .NET developer and I develop apps and expect from C# working automatic memory management! When I check my app with memory profiler I see no memory leaks in garbage collected managed .NET memory, so I don't do anything wrong. If native memory leak so much, it's a WinRT/.NET interop layer fault not mine.

    So, basically, Windows Runtime interop layer is leaking memory badly.




    • Edited by Muipo Monday, September 12, 2016 6:23 PM
    Monday, September 12, 2016 11:21 AM

All replies

  • Some example from stack overflow of mysterious native memory leaks when doing WinRT/.NET interop

    http://stackoverflow.com/questions/34834934/how-do-you-access-an-images-metadata-without-causing-a-leak-in-native-memory

    Seriously, it looks like critical interop flaw.

    Monday, September 12, 2016 12:40 PM
  • And some example from me.

    public static async Task CopyStreamAsync(this IInputStream src, IOutputStream outStream, uint bufferSize = 16384)  
    {  
        IBuffer buf2;  
        do  
        {  
            var buf = new Buffer(bufferSize);  
            buf2 = await src.ReadAsync(buf, bufferSize, InputStreamOptions.None);  
            if (buf2.Length > 0)  
            {  
                await outStream.WriteAsync(buf2);  
            }  
        } while (buf2.Length > 0);  
    }  

    This code leak native memory. When I analyzed it with native memory diagnostics, it shows that Buffer instance is not freed despite reference to it doesn't leave this method and is internal to method. For pure .NET this code is correct and shouldn't leak memory. But native Buffer object leaks memory and not freed, and there is no methods to prevent native memory leak - it haven't Dispose method.



    • Edited by Muipo Monday, September 12, 2016 12:50 PM
    Monday, September 12, 2016 12:49 PM
  • Can you provide an example of what you are doing that is causing the memory leak?
    • Edited by mcosmin Monday, September 12, 2016 12:54 PM
    Monday, September 12, 2016 12:54 PM
  • Provided simple example already.

    Look at the code above. It leaks. If you're .NET programmer cause of memory leak is totally unclear. This code shouldn't leak if it was pure .NET, but it leak native memory as Buffer object is native WinRT object.

    Update:

    If you analyze managed heap you will find no memory leaks. But if you analyze native heap - you will see native memory leak here. To catch these leaks you should run diagnostics tool in "Managed+Native Mixed" mode.

    • Edited by Muipo Monday, September 12, 2016 1:00 PM
    Monday, September 12, 2016 12:56 PM
  • The leak in your case is pretty obvious, you are allotting without ever disposing (see code). This is a common misconception, and the documentations do not really point out in the right direction. You need to use DataReader and DataWriter to do operations on a buffer. Direct creation of Buffer with new should never be done in code.

    var buf = new Buffer(bufferSize);  

    As a pure .net developer, you should know all references are allotted on the heap, and are only freed when GC runs, not when control leaves the body method (you are confusing value classes - on stack - with reference classes - on heap). As a .net developer, you should also know the GC can be notified of native allocation through AddMemoryPressure method. There is no memory leak, GC simply does not run in time.

    That being said, the only GC issue I ever encountered with winRT and .net was converting between byte[] to IBuffer using the extension method for byte[]. I could never figure that one out, even with AddMemoryPressure.
    • Edited by mcosmin Monday, September 12, 2016 1:05 PM
    Monday, September 12, 2016 1:03 PM
  • Please, don't use your irony here.

    I, naturally, know that GC should be run to free managed memory. You haven't read my message. I forced GC before taking snapshot in diagnostic tool, it's an obvious thing.

    And I must repeat again - managed memory doesn't leak at all. What are you talking about? I pointed several times to it - native memory, not managed memory leak!!!!

    Should I write it ten times? Sorry for emotional answer, but your irony about "GC developer should know..." isn't  polite here. Try to treat people nice if you don't know much about their education and professional level. Please.

    Direct creation of Buffer with new should never be done in code.

    Can you say why? Looks like WinRT/.NET interop flaw.

    The leak in your case is pretty obvious, you are allotting without ever disposing (see code).

    Buffer object haven't Dispose method. It isn't disposable.

    There is no memory leak, GC simply does not run in time.
    Managed memory doesn't leak. I have forced GC to run by obvious GC.Collect.
    Monday, September 12, 2016 1:14 PM
  • And I just posted above some piece of native memory leaking code.

    There are much native memory leaks in app without any managed memory leaks.

    Just illustrated it by some extremely simple piece of code.

    These native memory leaks are completely unclear for pure .NET developer. I must admit, I haven't deep technical knowledge about native COM and COM interop. As a pure .NET developer I just expect no problems here.

    Monday, September 12, 2016 1:18 PM
  • Ok, let me explain to you how this works.

    When you do

    var x = new SomewinRTNativeType();

    several things happen, but the most important are:

    1) Native allocation by calling the appropriate constructor.

    2) Reference is obtained in native world to this object. This reference is then passed to the .net interop layer. From .net's point of view, the object you just allotted only needs 32/64 bits of data for the pointer.

    3) The ref counter of the native object is 1 at this point (the interop layer holds a reference), but only stays so in the native world.

    Now, having the ref count of 1, the native object will not self destruct and will not release its memory, not matter what. It will do that when the ref count reaches 0, and that only happens if and only if the .net GC collects the interop layer reference. The memory leak does occur in the .net world. There may be several reasons why the GC does not collect your reference. I will come back to you once I have some more answers :)

    Monday, September 12, 2016 1:31 PM
  • It will do that when the ref count reaches 0, and that only happens if and only if the .net GC collects the interop layer reference. The memory leak does occur in the .net world.
    How about forced garbage collection by calling GC.Collect before taking memory snapshot in the diagnostic tool? I, naturally, have performed full GC to have clean picture. So, it isn't issue on managed side, I think. As I have performed full GC, no managed references should exist!

    • Edited by Muipo Monday, September 12, 2016 1:35 PM
    Monday, September 12, 2016 1:34 PM
  • Oh, I think I found your problem ^^

    Sorry, I've been very sleepy today, even though I had a full night's sleep :(

    You are creating a circular reference between buf and buf2, thus preventing GC and ref count self destruct.

    https://msdn.microsoft.com/en-us/library/windows/apps/windows.storage.streams.iinputstream.readasync

    Check the docs, they actually do recommend using DataReader ^^ - glad they got their act together, this is probably because the ReadAsync method of IInputStream is actually meant only for internal framework usage.

    Monday, September 12, 2016 1:47 PM
  • OK. I understand the cause of this memory leak in this example.

    But it anyway is WinRT/.NET interop flaw because there are some unclear rules for .NET world.

    If these objects be pure .NET, there will be no memory leaks at all, because .NET garbage collector safely collect any objects no matter how it created or used. There are some leaks in .NET, but they probably caused by attaching event handlers to static instances or something like. Memory leak inside method if all references inside method are local is an utterly crazy situation for .NET



    • Edited by Muipo Monday, September 12, 2016 1:59 PM
    Monday, September 12, 2016 1:59 PM
  • Well, another example of native memory leaks here.

    If you attach .NET event handler to XAML control in C# code, you should remove it manually in control Unloaded event handler (also removing Unloaded handler itself) or native memory should leak. Seems like event handlers assigned in XAML doesn't leak, but event handlers assigned in code leak if you don't remove they manually.

    I have this sort of leak already and fixed it by removing manually assigned XAML controls event handlers in Unloaded event handler. It's very counter-intuitive.

    It's time for some C# code now:

    Manually assigned event handlers in user control code-behind:

            public MarkupRenderControl()
            {
                this.InitializeComponent();
                Loaded += MarkupRenderControl_OnLoaded;
                Unloaded += MarkupRenderControl_OnUnloaded;
                SizeChanged += MarkupRenderControl_OnSizeChanged;
            }


    Unloaded event handler code:

            private void MarkupRenderControl_OnUnloaded(object sender, RoutedEventArgs e)
            {
                _isUnloaded = true;
                Loaded -= MarkupRenderControl_OnLoaded;
                Unloaded -= MarkupRenderControl_OnUnloaded;
                SizeChanged -= MarkupRenderControl_OnSizeChanged;
                RenderHost?.RemoveFromVisualTree();
                RenderHost = null;
                RenderData = null;
            }

    If you don't remove event handlers:

                Loaded -= MarkupRenderControl_OnLoaded;
                Unloaded -= MarkupRenderControl_OnUnloaded;
                SizeChanged -= MarkupRenderControl_OnSizeChanged;

    ...native memory will leak!

    Not sure about Loaded and Unloaded handlers, but I have found in native heap snapshot that unsubscribed SizeChanged handler indeed cause native memory leak.


    • Edited by Muipo Monday, September 12, 2016 2:11 PM
    Monday, September 12, 2016 2:10 PM
  • Some interesting article about Windows Runtime and .NET interop and XAML controls

    http://microsoft.github.io/Win2D/html/RefCycles.htm

    A cycle occurs when an object A has a reference to B, at the same time as B also has a reference to A. Or when A references B, and B references C, while C references A, etc.

    When subscribing to events of a XAML control, this sort of cycle is pretty much inevitable:

    • XAML page holds references to all the controls contained within it
    • Controls hold references to the handler delegates that have been subscribed to their events
    • Each delegate holds a reference to its target instance
    • Event handlers are typically instance methods of the XAML page class, so their target instance references point back to the XAML page, creating a cycle

    If all the objects involved are implemented in .NET, such cycles are not a problem because .NET is garbage collected, and the garbage collection algorithm is able to identify and reclaim groups of objects even if they are linked in a cycle.

    Unlike .NET, C++ manages memory by reference counting, which is unable to detect and reclaim cycles of objects. In spite of this limitation, C++ apps using Win2D have no problem because C++ event handlers default to holding weak rather than strong references to their target instance. Therefore the page references the control, and the control references the event handler delegate, but this delegate does not reference back to the page so there is no cycle.

    The problem comes when a C++ WinRT component such as Win2D is used by a .NET application:

    • The XAML page is part of the application, so uses garbage collection
    • The Win2D control is implemented in C++, so uses reference counting
    • The event handler delegate is part of the application, so uses garbage collection and holds a strong reference to its target instance

    A cycle is present, but the Win2D objects participating in this cycle are not using .NET garbage collection. This means the garbage collector is unable to see the entire chain, so it cannot detect or reclaim the objects. When this occurs, the application must help out by explicitly breaking the cycle. This can be done either by releasing all references from the page to the control (as recommended above) or by releasing all references from the control to event handler delegates that might point back to the page (using the page Unloaded event to unsubscribe all event handlers).

    Win2D library provide some useful XAML controls implemented in C++ (as all standard Windows XAML controls also implemented in C++), and Win2D documentation have special page about memory leaks in .NET apps when using native XAML controls and how to avoid it.

    Incredible! As I can conclude here, WinRT/.NET interop is not mature technology and have obvious flaws. There are too many issues with it.




    • Edited by Muipo Monday, September 12, 2016 2:16 PM
    Monday, September 12, 2016 2:15 PM
  • Well, another example of native memory leaks here.

    If you attach .NET event handler to XAML control in C# code, you should remove it manually in control Unloaded event handler (also removing Unloaded handler itself) or native memory should leak. Seems like event handlers assigned in XAML doesn't leak, but event handlers assigned in code leak if you don't remove they manually.

    I have this sort of leak already and fixed it by removing manually assigned XAML controls event handlers in Unloaded event handler. It's very counter-intuitive.

    It's time for some C# code now:

    Manually assigned event handlers in user control code-behind:

            public MarkupRenderControl()
            {
                this.InitializeComponent();
                Loaded += MarkupRenderControl_OnLoaded;
                Unloaded += MarkupRenderControl_OnUnloaded;
                SizeChanged += MarkupRenderControl_OnSizeChanged;
            }


    Unloaded event handler code:

            private void MarkupRenderControl_OnUnloaded(object sender, RoutedEventArgs e)
            {
                _isUnloaded = true;
                Loaded -= MarkupRenderControl_OnLoaded;
                Unloaded -= MarkupRenderControl_OnUnloaded;
                SizeChanged -= MarkupRenderControl_OnSizeChanged;
                RenderHost?.RemoveFromVisualTree();
                RenderHost = null;
                RenderData = null;
            }

    If you don't remove event handlers:

                Loaded -= MarkupRenderControl_OnLoaded;
                Unloaded -= MarkupRenderControl_OnUnloaded;
                SizeChanged -= MarkupRenderControl_OnSizeChanged;

    ...native memory will leak!

    Not sure about Loaded and Unloaded handlers, but I have found in native heap snapshot that unsubscribed SizeChanged handler indeed cause native memory leak.



    This is expected behaviour. Event handlers create strong links, and you need to unsubscribe them. These are not obvious flaws, these are direct results of how each technology work.
    Monday, September 12, 2016 3:06 PM
  • You are creating a circular reference between buf and buf2, thus preventing GC and ref count self destruct.

    Where? I don't see any reference between created from one buffer to the other. I don't even see how buffers could take references to other buffers.
    Monday, September 12, 2016 3:18 PM
  • This is utterly unexpected behavior for the .NET world!

    In pure .NET unsubscribed event handlers doesn't cause leaks as objects anyway are collected by GC no matter how they reference each other. If both objects (object with event and event subscriber) isn't referenced by any other object they both will be collected by garbage collector. It's how .NET automatic memory management works.

    If it's not a case when you attach event handler to native XAML control - it's very unfamiliar to .NET developers. .NET developers expect familiar behavior and when event handlers works according to COM rules it cause great confusion, boilerplate code, code style inconsistency. This code is very error-prone for .NET developers as it require manual memory management in automatic memory managed world!

    And what is worse this behavior is implementation-dependent! If you use XAML control or component created in C#, there will be no event handler memory leak issues. If you use XAML control or component created in C++ you will get memory leaks.

    This is expected behaviour.

    This behavior is expected by C++ COM developer. But for .NET developer this behavior is totally unexpected. And (as I pointed above) this behavior (to make things worse) is implementation-dependent!

    So, we again going to the point where WinRT/.NET interop layer have flaws, is incomplete and doesn't provide .NET developers expected implementation-independent behavior.





    • Edited by Muipo Monday, September 12, 2016 3:25 PM
    Monday, September 12, 2016 3:19 PM
  • This is NOT expected behavior.

    .NET developers don't expect it because no other .NET UI framework has this limitation.

    C++ developers don't expect it because "C++ event handlers default to holding weak rather than strong references to their target instance."

    .NET needs weak events right now, or this technology just won't work right.

    Monday, September 12, 2016 3:23 PM
  • Are you sure that code it right?

    I ask because in WPF it definitely wouldn't be, as controls may be loaded and unloaded multiple times.

    Monday, September 12, 2016 3:24 PM
  • @Jonathan Allen

    This code is right and it works in actual UWP app. As far as I know Unloaded event in UWP XAML is serving same purpose as Dispose in IDisposable pattern would. After Loaded event fired it's safe to access visual tree and Unloaded event handler is a place when you can safely clean up things. So this XAML control lifecycle is quite different from WPF.

    Anyway this code works in actual app.


    • Edited by Muipo Monday, September 12, 2016 3:34 PM
    Monday, September 12, 2016 3:33 PM
  • .NET needs weak events right now, or this technology just won't work right.

    I think it's a bug in the WinRT/.NET event interop. To .NET world C++ XAML controls are visible as a thin .NET runtime wrappers. So, if you subscribe event to wrapper object this wrapper object should behave like any ordinary .NET object. It should wrap event handlers and subscribe to underlying COM object according to COM rules and provide expected .NET behavior to the outside .NET subscribers. So wrapper object should remove underlying COM event handler if event handler is manually unsubscribed from .NET OR if wrapper object is collected by .NET garbage collector.

    Generated .NET wrapper interop objects should use ~ finalizer. They should free all native resources (including unsubscribed COM event handlers) when garbage collector collect .NET wrapper for COM object.






    • Edited by Muipo Monday, September 12, 2016 3:48 PM
    Monday, September 12, 2016 3:41 PM
  • @Jonathan Allen

    This code is right and it works in actual UWP app. As far as I know Unloaded event in UWP XAML is serving same purpose as Dispose in IDisposable pattern would. After Loaded event fired it's safe to access visual tree and Unloaded event handler is a place when you can safely clean up things. So this XAML control lifecycle is quite different from WPF.

    Anyway this code works in actual app.



    Nope, it is not. the "memory leak" is caused by the Window - Frame - Page relationship, not the event handlers, and no Unloaded does not work like IDisposable, it just happens that when you set the Window content to null, the UI gets unloaded (which is natural). Event handlers attached to same class do not cause "memory leaks".
    Monday, September 12, 2016 3:54 PM
  • Nope, it is not. the "memory leak" is caused by the Window - Frame - Page relationship, not the event handlers, and no Unloaded does not work like IDisposable, it just happens that when you set the Window content to null, the UI gets unloaded (which is natural). Event handlers attached to same class do not cause "memory leaks".

    No. This posted above code with event handlers isn't from Page but from UserControl implementation. And it does leak if event handlers aren't unsubscribed at Unloaded event handler. It's a tested case. Today I have updated nuget package addressing memory leak issue. So there is no Window-Frame-Page relationship in UserControl. When user navigate from one page to another Page is collected and so collected all XAML tree including this UserControl. If it's placed inside ListView data template, Unloaded event doesn't fire because ListView by default reuse data template items, but fire Unloaded on parent page unload.

    Anyway it doesn't matter. If Window content is set to null, Unloaded event is fired in a Dispose-like manner (resources can be safely cleaned here).

    But! If you don't unsubscribe these events, memory would leak even if UI content have already cleaned and unloaded.

    This issue is very well surface itself if you play with navigation UI and do many navigations between pages forward and backward and then take memory snapshot in the diagnostic tool.







    • Edited by Muipo Monday, September 12, 2016 4:15 PM
    Monday, September 12, 2016 4:10 PM
  • All UI elements have circular relationships with their parents.
    Monday, September 12, 2016 4:15 PM
  • @mcosmin

    Could you tell here, are WinRT/.NET generated interop wrappers using .NET finalizers to clean up native COM references?

    Classic .NET wrapper around Windows handle close handle at finalizer if it isn't closed already at Dispose method call.

    After all, .NET finalizers in the first place was created to clean up resources unreachable by garbage collector when wrapper object is collected.

    According to WinRT/.NET interop behavior this classic finalizer pattern isn't used by WinRT generated wrapper objects so there are issues that require manual memory management from .NET developer.





    • Edited by Muipo Monday, September 12, 2016 4:22 PM
    Monday, September 12, 2016 4:21 PM
  • All UI elements have circular relationships with their parents.

    And so what?

    If all UI XAML tree is unloaded after page navigation this shouldn't matter!

    But memory leak even after you navigate from the page completely.

    In the case you ask it, this interop behavior is tested with frame navigation cache disabled.



    • Edited by Muipo Monday, September 12, 2016 4:36 PM
    Monday, September 12, 2016 4:25 PM
  • If anyone forget about .NET finalizers and Disposable pattern implementation in .NET - here some links to MSDN:

    Disposable pattern

    Finalizable types are types that extend the Basic Dispose Pattern by overriding the finalizer and providing finalization code path in the Dispose(bool) method.

    Finalizers are notoriously difficult to implement correctly, primarily because you cannot make certain (normally valid) assumptions about the state of the system during their execution. The following guidelines should be taken into careful consideration.

    Note that some of the guidelines apply not just to the Finalize method, but to any code called from a finalizer. In the case of the Basic Dispose Pattern previously defined, this means logic that executes inside Dispose(bool disposing) when the disposing parameter is false.

    If the base class already is finalizable and implements the Basic Dispose Pattern, you should not override Finalize again. You should instead just override the Dispose(bool) method to provide additional resource cleanup logic.

    The following code shows an example of a finalizable type:

    public class ComplexResourceHolder : IDisposable {
    
        private IntPtr buffer; // unmanaged memory buffer
        private SafeHandle resource; // disposable handle to a resource
    
        public ComplexResourceHolder(){
            this.buffer = ... // allocates memory
            this.resource = ... // allocates the resource
        }
    
        protected virtual void Dispose(bool disposing){
                ReleaseBuffer(buffer); // release unmanaged memory
            if (disposing){ // release other disposable objects
                if (resource!= null) resource.Dispose();
            }
        }
    
        ~ ComplexResourceHolder(){
            Dispose(false);
        }
    
        public void Dispose(){
            Dispose(true);
            GC.SuppressFinalize(this);
        }
    }

    Article about finalizers

    The Finalize method is used to perform cleanup operations on unmanaged resources held by the current object before the object is destroyed. The method is protected and therefore is accessible only through this class or through a derived class.

    Monday, September 12, 2016 4:52 PM
  • I should also remind you that in classic .NET any object holding native resources should implement IDisposable pattern and should release unmanaged resources at finalizer if object already isn't disposed.

    But UWP WinRT/.NET interop doesn't follow this rule. Automatically generated wrapper objects aren't disposable and aren't release resource on garbage collection at finalizer.





    • Edited by Muipo Monday, September 12, 2016 5:02 PM
    Monday, September 12, 2016 5:00 PM
  • >But UWP WinRT/.NET interop doesn't follow this rule. Automatically generated wrapper objects aren't disposable and aren't release resource on garbage collection at finalizer.

    This has always been the design of .NET COM Interop.  RCWs are not Disposable and rely on a Finalizer to decrement the reference count on the referenced COM object.

    The problem is that .NET objects are _not_ reference counted and the runtime has no idea how many references you have to a RCW in your AppDomain.  So the safe solution is to wait until the RCW is unreachable and then put it on the Finalization Queue. 

    This can lead to increased memory consumption as the referenced COM objects live for longer than a managed object would, and don't always trigger GC the way managed objects would. 

    But it's not technically a memory leak as the finalizer will _eventually_ decrement the reference count.

    David


    David http://blogs.msdn.com/b/dbrowne/

    Monday, September 12, 2016 5:11 PM
  • @davidbaxterbrowne

    I understand that .NET objects are not reference counted. And RCW should release reference at finalizer.

    But according to WinRT/.NET interop behavior in UWP it just don't work.

    It is probably a bug in WinRT interop code preventing native resource from releasing even after full garbage collection triggered by GC.Collect after successful navigation from a XAML page and there is no references left to unloaded page from the outside!

    And how about example with Buffer above? Local variable holding IBuffer reference cause memory leak as it isn't released at all.

    Seems like WinRT/.NET interop layer have unnoticed bugs.






    • Edited by Muipo Monday, September 12, 2016 5:23 PM
    Monday, September 12, 2016 5:18 PM
    1. COM assumes you won’t be holding onto the object reference when the method returns;
    2. The RCW assumes you *will* be holding onto the object reference when the method returns.
    /// in .NET, 
    /// when this RunActiveAction returns, still keeps an extra reference to pActionRCW
    static void RunActiveAction(IQTTest pTestRCW)
    {
        IQTAction pActionRCW;
        pTestRCW.GetActiveAction(out pActionRCW);
        pTestRCW.RunAction(pActionRCW);
    }


    So in this article call to Marshal.FinalReleaseComObject is suggested.

    This article clearly explain why posted above example with IBuffer leak.

    Seriously, COM/.NET interop as it works today is flawed and counter-intuitive.

    In classic .NET it was rarely used, but in UWP COM/.NET interop is used heavily and became major developer issue.


    • Edited by Muipo Monday, September 12, 2016 6:04 PM
    Monday, September 12, 2016 5:59 PM
  • Official comments from Microsoft about these issues are needed.

    These platform issues shouldn't be underestimated. As of today we have relatively low-quality WinRT/.NET interop and it's a common knowledge amongst UWP platform developers. Even MSDN suggests developer to avoid WinRT/.NET interop on UWP if it is possible. It's easy to say but hard to implement as all UWP platform API surface is implemented as native WinRT, including XAML UI framework.

    So, low-quality WinRT/.NET interop literally means low-quality platform development experience in general as UWP API is naturally based on COM including XAML UI framework. It's time to wake up and start to hear what developers are saying.


    • Edited by Muipo Monday, September 12, 2016 10:59 PM
    Monday, September 12, 2016 10:58 PM
  • >So in this article call to Marshal.FinalReleaseComObject is suggested.

    >This article clearly explain why posted above example with IBuffer leak.

    >Seriously, COM/.NET interop as it works today is flawed and counter-intuitive.

    >In classic .NET it was rarely used, but in UWP COM/.NET interop is used heavily and became major developer issue.

    I agree with each of these statements.  Another reason that this is important in UWP is that UWP is all about programming for devices of varying size.  .NET desktop and server programming often benefit from high minimum system requirements.

    GC gives you the illusion of infinite memory, and that illusion breaks down more easily in when a process (especially a 32bit process) uses a lot of unmanaged memory.   Freeing memory in finalizers is fundamentally unreliable.

    If your app allocates a whole lot of unmanaged memory (relative to the target device), then you may need to intervene in the memory management by manually calling ReleaseComObject or by forcing GC at times you know that the managed heap contains lots of unreachable RCWs accounting for large amounts of unmanaged memory, or by calling GC.AddMemoryPressure(int) after allocating unmanaged memory.

    IMO creating IDisposable wrappers that call ReleaseComObject for critical resources is a can be good pattern to force you to manage the runtime of resources explicitly.

    David


    David http://blogs.msdn.com/b/dbrowne/


    Monday, September 12, 2016 11:01 PM
  • In my opinion, fundamental flaw is because not any side is know about each other and interop is implemented in a very conservative way.

    .NET garbage collector should treat COM objects as a first-class citizens. It should know that object is reference-counted and so .NET runtime should increment reference count once and decrement it as a part of garbage collection process automatically without need to wait for finalizer queue.

    Alternatively, basic COM model should be extended to support garbage collector, maybe by implementing additional COM interface for extended memory management support. If COM object doesn't implement this interface, fallback RCW should be used. Obviously, Universal Windows API classes should support this extended memory management interface. This support should be optional for backward compatibility reasons or when external memory management is harmful.

    COM class supporting this interface should report its memory consumption, references to other COM objects and should ignore reference counting if external memory management is turned on for this object. In other words, COM should generally support garbage collector and provide any information for it via standard COM interface which can be used by external garbage collector if it's present. If it isn't present or not enabled for this particular COM object, fallback reference counting should be used instead.

    Monday, September 12, 2016 11:42 PM
  • Most serious problem with COM and UWP is - COM itself haven't evolved for so long time. COM absolutely must support garbage collected memory management, at least as an option by introducing new interfaces which if implemented could allow COM objects to be garbage collected by external memory manager instead of built-in internal reference counted memory management. All modern languages are garbage collected, even C++ standards have foundation for garbage collected memory management. COM indeed have potential to remain low-level foundation for Windows platform, but it should evolve to support modern development needs.
    Tuesday, September 13, 2016 8:20 AM
  • Man, I understand you're having a little issues adapting to UWP, but chill. Asking to make COM, an unmanaged, language independent framework, garbage collected is pure madness. There are a LOT of implications around having an nondeterministic memory model in a low level language designed to be deterministic. The prime objective of COM is to provide an application model that is independent of languages. There are ways to avoid the so called "memory leaks" in .net framework, and you should start learning them. It will take far less time than rewriting COM to fit a nondeterministic model. And unsubscribing event handlers (which is what you should be doing in .net anyway, to ease the GC job) is really much easier than using the Marshall class. I can understand there is a learning curve when having to develop applications that run on constrained memory models when coming from the limitless possibilities of the x86 world, but you need to start taking everything into moderation.

    I have been developing high performance UWP/winRT apps ever since this platform came to be and I never ever had memory issues unless I did things I did not know enough about.

    Tuesday, September 13, 2016 10:26 AM
  • @mcosmin

    Main issue with it is that UWP require manual memory management from .NET developer.

    One of the most exciting aspects of .NET as a platform is its transparent memory management which require precisely zero effort from developer. You underestimate its impact on developer productivity. When UWP platform require manual memory management from developer - it brings huge negative impact on developer productivity.

    It is not a "learning curve" issue for me. I have started my developer career as Borland Delphi (on Windows) and C (on UNIX-like systems) developer and I surely know what manual memory management essentially is.

    For me, comeback of manual memory management on UWP platform is an obvious and overwhelming regression in productivity and overall developer experience. After years of pure .NET development I remember manual memory management as a nightmare I wish to forget. Usage of weak event pattern, weak references, manual event unsubscription, inventing own homebrew weak reference based page lifetime services and so on is a huge amounts of boilerplate code, huge source of head ache. It generally brings productivity down.

    >There are ways to avoid the so called "memory leaks" in .net framework, and you should start learning them.

    No, no. Not "so called". These leaks in UWP are real and surface itself every time you dare to implement non-trivial app and XAML UI model. And COM interop is actually leak because of flaws in RCW implementation.

    Let's see posted above example, again:

    public static async Task CopyStreamAsync(this IInputStream src, IOutputStream outStream, uint bufferSize = 16384)  
    {  
        IBuffer buf2;  
        do  
        {  
            var buf = new Buffer(bufferSize);  
            buf2 = await src.ReadAsync(buf, bufferSize, InputStreamOptions.None);  
            if (buf2.Length > 0)  
            {  
                await outStream.WriteAsync(buf2);  
            }  
        } while (buf2.Length > 0);  
    }  

    As I found, root cause of memory leak is

    1. COM assumes you won’t be holding onto the object reference when the method returns;
    2. The RCW assumes you *will* be holding onto the object reference when the method returns.

    It is not "finalizer" issue. This IBuffer instance is not freed forever, it's a classical memory leak.

    Root cause for this leak is a RCW implementation which leave reference count 1 if you assign WinRT object to local variable and doesn't return it from the function. How many people know this? Call to Marshal.FinalReleaseComObject is required for this buffer to be freed, according to the article. If you don't decrease reference count manually, this IBuffer instance will remain in memory forever, even after managed RCW wrapper itself is collected by GC. Do you understand this? Do you read this thread carefully?

    >I have been developing high performance UWP/winRT apps ever since this platform came to be and I never ever had memory issues unless I did things I did not know enough about.

    As I posted in first post, managed memory doesn't leak. You can't catch memory leaks analyzing managed heap as there are many cases when managed wrappers for COM objects are collected by GC, but underlying native COM objects remain in memory and add overall memory usage.

    Example with IBuffer doesn't leak managed memory. If you snapshot managed memory you will see no traces of IBuffer. But if you analyze native heap, you will see many orphaned native COM Buffer instances just because

    1. COM assumes you won’t be holding onto the object reference when the method returns;
    2. The RCW assumes you *will* be holding onto the object reference when the method returns.

    This forum thread is about native memory leaks because of incomplete and flawed WinRT/.NET interop on UWP platform.


    • Edited by Muipo Tuesday, September 13, 2016 11:25 AM
    Tuesday, September 13, 2016 11:20 AM
  • >will remain in memory forever, even after managed RCW wrapper itself is collected by GC.

    No it won't.  This is not a "classical memory leak".  It's just the RCWs relying on the finalizer to free unmanaged memory. 

    The unmanaged memory used by the buffers will be cleaned up by the finalizer.  The issue is that you still may run out of memory if the GC doesn't run frequently enough.  With 100% managed memory GC always prevents you from running out of memory as allocations trigger GC.  But here GC doesn't automatically kick in to (indirectly) free the unmanaged memory. 

    eg here are various options.  If you force GCs the process committed memory does not grow.  Both adding memory pressure and manually releasing the COM object will work too.

       private void Button_Click(object sender, RoutedEventArgs e)
            {
                var mem = GC.GetTotalMemory(true);
                var bufferList = new List<Windows.Storage.Streams.Buffer>();
    
                for (int i = 0; i < 1000; i++)
                {
                    var buf  = new Windows.Storage.Streams.Buffer(1024 * 1024);
                    totalBuffers += 1;
                    var bl = buf.Capacity;
                    //GC.AddMemoryPressure(bl);
                    bufferList.Add(buf);
                    //System.Runtime.InteropServices.Marshal.ReleaseComObject(buf);
                }
                Int64 mem2;
                
                var memBeforeGC = GC.GetTotalMemory(false);
                bufferList.Clear();
    
                if (ForceGC.IsChecked.Value)
                {
                    GC.Collect(2, GCCollectionMode.Forced, true);
                    GC.WaitForPendingFinalizers();
                    GC.Collect(2, GCCollectionMode.Forced, true);
                    mem2 = GC.GetTotalMemory(true);
                }
                else
                {
                    mem2 = GC.GetTotalMemory(false);
                }
                this.TextBlockOutput.Text = $@"
    Mem {mem}, Mem2 {mem2} Total Buffers {totalBuffers}
    Pre GC mem {memBeforeGC}";
            }

    David


    David http://blogs.msdn.com/b/dbrowne/

    Tuesday, September 13, 2016 11:31 AM
  • @davidbaxterbrowne

    No, you are wrong.

    This code is actually leaking memory. I have tested this issue of course doing full GC manually before taking snapshot. GC doesn't help at all.

    public static async Task CopyStreamAsync(this IInputStream src, IOutputStream outStream, uint bufferSize = 16384)  
    {  
        IBuffer buf2;  
        do  
        {  
            var buf = new Buffer(bufferSize);  
            buf2 = await src.ReadAsync(buf, bufferSize, InputStreamOptions.None);  
            if (buf2.Length > 0)  
            {  
                await outStream.WriteAsync(buf2);  
            }  
        } while (buf2.Length > 0);  
    }  


    If you look at this code piece as .NET developer you will find nothing wrong.

    >If you force GCs the process committed memory does not grow.  Both adding memory pressure and manually releasing the COM object will work too.

    No. Posted above code piece leak native memory and no GC could release memory, you can call GC million times.

    And so UWP apps actually leak native memory on many cases and no manual GC helps here. Because managed memory doesn't leak.

    UPDATE:

    "Buffer leak" example isn't true. Leak isn't repro in isolated example app.

    • Edited by Muipo Tuesday, September 13, 2016 4:41 PM
    Tuesday, September 13, 2016 11:40 AM
  • Interoping .net with unmanaged stuff always required manual memory management. Working with streams and buffers in .net always required you to manually call the Dispose method to clean up the memory.

    As I said, the documentation does lack some of the high performance stuff that should be mentioned, but there is not a memory leak, nor a design issue with winRT that you have to release unmanaged memory manually. At least the docs do mentioned that winRT is a native framework.

    Tuesday, September 13, 2016 12:21 PM
  • >This code is actually leaking memory.

    Can you post a more complete repro of this scenario?  The code I posted allocates lots of buffers, but does not leak.

    David


    David http://blogs.msdn.com/b/dbrowne/

    Tuesday, September 13, 2016 12:29 PM
  • @mcosmin

    It would be great if WinRT was only about streams and buffers. In reality things are much grim.

    Entire XAML UI framework is native COM-based. Given complexity of typical non-trivial XAML UI, situation quickly goes out of control. Typical XAML UI is a heavy mix of native COM and managed .NET classes having massive cross references, data bindings, XAML behaviors, data templates, value converters, animations and so on. Your "all native resources should be manually managed" doesn't apply here.

    Tuesday, September 13, 2016 12:32 PM
  • Data bindings, XAML behaviors, data templates, value converters, animations and so on are all winRT objects. What you use in .net are the stuff from the System namespace (collections, primitives, threading synchronization stuff). UI is automatically handled by the system, it almost never interacts with true managed objects unless you write your code that way.

    Tuesday, September 13, 2016 12:56 PM
  • @mcosmin

    And again, you are wrong. XAML UI in actual app indeed is a heavy mix of native and managed objects. System provide only reusable components, not actual XAML UI logic nor view models behind it.

    And you are wrong about many components you listed as "native".

    >data templates

    Any data template item is connected with underlying .NET view model and probably with several custom user controls, which are created as .NET managed classes in app code.

    >value converters

    XAML framework doesn't provide much practical value converters implementation. You probably implement IValueConverter interface in .NET code to do app-specific work.

    >XAML behaviors

    It is a managed standard .NET library from nuget repository and isn't part of native XAML UWP framework itself.

    >Data bindings

    Strongly-typed {x:Bind ...} data bindings essentially are generated by Visual Studio code-behind .NET classes placed in \obj\ folder.

    So you're probably don't understand scale of the disaster.







    • Edited by Muipo Tuesday, September 13, 2016 1:16 PM
    Tuesday, September 13, 2016 1:07 PM
  • UWP platform is a perfect example for Joel Spolsky's law of leaking abstractions. Native WinRT/.NET interop layer literally leak as hell.
    Tuesday, September 13, 2016 1:29 PM
  • >This code is actually leaking memory.

    Can you post a more complete repro of this scenario?  The code I posted allocates lots of buffers, but does not leak.

    Here is example app.

    Well. This example doesn't leak. "Buffer leak" case taken in isolated environment proven to be false.

    Tuesday, September 13, 2016 4:37 PM
  • Hi Muipo,

    Thanks for taking the time to report the issues. I work at Microsoft and I'm from the .NET team focusing on C# interop (WinRT/COM/pinvokes/etc). There are a lot of interesting discussion in this thread, and I'd like to help clarify some of the issues that we are discussing here.

    First, Windows Runtime is based on COM, which means its lifetime is still largely (but not completely, as we have technology that address cycles - see below) based on ref-counts, which are naturally subject to cycles. This is not C# specific - any language that interop with COM components can have circular reference between COM objects, and the cycles needs to be manually resolved by releasing one of them, or making one of the references a weak reference (not keeping ref count), and therefore "break the cycle". 

    C# and .NET, being a managed environment, has the capability to collect managed objects with cycles. However, when you are interop-ing with other component technology (like COM), you are subject to the lifetime rules of that particular technology, and in this case, ref counts and cycles. They are a package deal. The reason GC can resolve cycles is because GC is aware of all graph references, strong and weak, within the managed heap, and it has enough information to know if a circular graph is no longer being referenced (through marking). However, with COM and WinRT interop, external references are being tracked as ref-counts, not graphs, and therefore GC doesn't have enough information to automatically break the cycles. In GC terms, they are effectively roots (like static variables) forming cycles that you have to manually break by assigning null to them. 

    (BTW, having a GC doesn't mean you will never ever have a leak - for example, you could still end up with a static variable "rooting" a graph of objects and they'll never go away. )

    In particular, in C# COM/WinRT interop, RCWs are proxy objects that have strong ref-count (could be one or more, depending on internal cache) to the underlying COM object. 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. When you pass managed objects out to native COM code, the CLR creates a proxy called CCW that tracks external ref counts (from COM code) and keep the underlying managed object alive (if ref count > 0). You could end up with cycles if you have a RCW A -> external native COM object -> CCW B -> same RCW A. GC will not be able to resolve the cycles as it doesn't know when the CCW can be safely released - that is subject to the external COM object. 

    When developing the Windows Store app platform (and then UWP), we realized that having cycles would be a huge adoption blocker for people writing .NET apps using WinRT API, especially with XAML objects. We worked with XAML team and developed a technology together so that XAML can inform the CLR GC with missing reference/edge information between CLR and XAML objects. And with that, GC is able to resolve cycles between CLR objects and XAML objects, with the complete graph information. I think this is in line with what you are seeing - if you are using CLR objects with XAML, you don't need to unsubscribe the events and there is no leak. 

    Unfortunately, the C++ custom controls doesn't have the technology to inform GC of such missing references. And more interestingly, now we have 3 parties (or more) involved - C++, XAML, CLR, instead of just CLR and XAML. Right now we don't have a good way to support exchanging information between these different environments, which is why you are observing leaks with circular references between CLR and C++ custom control right now. This is a known limitation that we are looking into, and we'll share more information when we are ready. 

    Hope this helps. And please feel free to let me know if you have more questions.

    Thanks,
    Yi Zhang

    Wednesday, September 21, 2016 1:40 AM
  • 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.

    Wednesday, September 21, 2016 9:30 AM
  •    I read the whole thread with a lot of interest. The problem of memory leaks in UWP/WinRT/C# bothers me very much as well. I run into many of those issues myself and I'm sure I'm not aware of many other possibilities for the leaks too. I see people on this thread are really keen on the topic. Would you be interested, or better say, so kind, to look at two my posts regards memory leaks and give me your opinion or maybe a workaround for the situations? I don't know if those leaks could be classified as interop memory leaks or it is something entirely different related to the network communication.

       One post is regarding memory leaks in the StreamSocket connection https://social.msdn.microsoft.com/Forums/en-US/2aa78561-95cc-4b1e-b8b3-ca33cc403c44/is-there-a-workaround-for-the-handle-leak-in-streamsocket. I found this one not even in UWP environment but it is using WinRT methods from Windows Phone Silverlight application so it can be something related to interop I guess.

       The other post is again a leak in network communication but this time it happens while transferring data to/from StreamSocket https://social.msdn.microsoft.com/Forums/Windowsapps/en-US/33cad3cc-abcf-4568-83ba-db9a7bdf9109/uwp-how-to-find-a-memory-leak-in-this-case-and-can-it-be-in-task-object?forum=wpdevelop. I found this one in UWP environment.

       Most of all I'm interested if there is a workaround, anything at all. If I will need to call any native methods to free resources it is fine, I definitely can do that but I really would like to get rid of these leaks or minimize their impact as much as possible. Our application heavily uses network communication and it is a serious issue for us.

    Thank you


    Alex

    Thursday, September 22, 2016 2:16 AM
  • 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

    Thursday, September 22, 2016 5:00 AM
  • Hi Alex,

    Let's take the discussion over to the threads you've posted. I only get a chance to respond to one of them (and I don't see a leak there in Visual Studio 2015 Update 3 when putting the code in a UWP app), and posted more information there. I will look at the other one later when I get a chance. 

    Thanks,

    Yi Zhang

    Thursday, September 22, 2016 5:04 AM
  • Sure, I will try to give you a repro over the weekend :)
    Thursday, September 22, 2016 6:36 AM
  • @Yi Zhang

    What I exactly have observed is a really weird situation.

    When I analyze application with managed memory profiler it reports no memory leaks.

    Managed heap size is about 5Mb and don't growing much larger even after lots of navigation between pages and app usage. So, CLR managed memory profiler reports no leaks and managed heap analyze doesn't reveal any memory leaking. There was one source of managed memory leaks in CLR app - {x:Bind ...} (interesting, traditional runtime {Binding ...} don't leak at any circumstances, just change from {x:Bind ...} to {Binding ...} have automatically solved most managed memory leaks in app, but for better performance I have tuned {x:Bind ...} usage instead of fallback to traditional runtime bindings) compiled bindings, but these leaks was eliminated by some piece of manual memory management.

    So, no CLR managed memory leaks in app. But when I have observed native memory usage in process manager (private heap working set, total commit size, etc.), I have seen much different picture. Memory consumption was growing at danger rate and it quickly reached > 200Mb of private heap working set and keep growing after each page navigation - same time managed CLR heap size was changed predictably. If large amount of view model data was allocated (2-5Mb in size, I consider it is very large for application supposed to be used on ARM Windows 10 Mobile platform too), after navigation from such page that data was collected by GC. No weird things occurred. In the CLR managed heap world all was nice. But native memory usage (reported by process manager on PC and by process manager on device portal of Lumia 950 device) was awful reaching >200Mb and keep growing!

    When I profiled app in "Managed+Native mixed" mode, I have found lots of unallocated objects having weird mangled names and numbers instead of names. It is very hard and confusing to investigate anything in native heap. Natural logical conclusion is that leaks are occurring on COM side, not on CLR side. But as I didn't perform any custom COM interop, did not call any marshalling API and totally rely on built-in means to interop provided by .winmd for system framework APIs (native .winmd metadata, after all, provide enough information for compiler to do interop things automatically), I think there are some unnoticed issues in built-in managed/native interop mechanics.

    I was tested this situation using standard Visual Studio memory profiler (as far as I know only standard Visual Studio profiler can profile native heap and report native memory issues - no other known to me 3rd party profilers can do it for UWP apps). I was tested it in .NET Core Runtime build mode and in .NET Native build mode.

    Native memory is leaking when CLR managed memory isn't. It is very strange and an obvious symptom of critical problems on the COM side.


    • Edited by Muipo Thursday, September 22, 2016 10:49 AM
    Thursday, September 22, 2016 10:30 AM
  • 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. 

    Wow! It's very interesting topic to discuss!

    Managed CLR Garbage Collector can move objects in memory and effectively eliminate heap fragmentation. So, for CLR world many memory allocations and unallocations isn't a problem. Heap fragmentation problem is addressed by GC automatically. But in native COM world situation is different. Native COM heap haven't any means to address native heap fragmentation. If XAML app is using many allocations - heap would become heavily fragmented and quickly became a mess. Typical scenario for it is a DataTemplate using inside ListView control. Other scenario for it is a lots of small images inside lists and data templates (it's a case for my app). As Image control is native COM control and bitmaps have rather small but considerable size measured in Kb, it would naturally fragment native heap.

    As far as I know, there is no automatic solution for native heap fragmentation. Managed heap have built-in mechanics for memory space defragmentation, but native heap haven't.

    Maybe it's a cause of excessive private heap working set growing in my app even if managed heap profiling doesn't report any managed memory leaks.


    Thursday, September 22, 2016 11:06 AM
  • When developing the Windows Store app platform (and then UWP), we realized that having cycles would be a huge adoption blocker for people writing .NET apps using WinRT API, especially with XAML objects. We worked with XAML team and developed a technology together so that XAML can inform the CLR GC with missing reference/edge information between CLR and XAML objects. And with that, GC is able to resolve cycles between CLR objects and XAML objects, with the complete graph information. I think this is in line with what you are seeing - if you are using CLR objects with XAML, you don't need to unsubscribe the events and there is no leak. 

    Maybe it would be benefical to all to make these mechanics publically available and standardized as an official public part of Windows Runtime/UWP platform?

    I have proposed above how traditional (and very ancient) COM model could be improved to help managed GC do its work. And as it's revealed now, UWP platform have some undocumented private mechanics to help managed GC in breaking ref count cycles!

    Traditional COM rely on internal memory management based on ref counting. But it wouldn't be much hard to introduce optional external memory management model for COM objects. COM object would implement optional COM interface to support external memory management (CLR Garbage Collector or C++ garbage collector as it's already available in C++ as an open source library, it may be anything as it would work via standardized COM interfaces). This COM interface (or set of COM interfaces) would report to external memory manager its memory consumption, references to other COM objects and other information GC require to do its work. If COM object is in external memory management mode (triggered by external memory manager) it would free memory not on reaching ref count = 0, but when external memory manager explicitly order it to do via external memory management interface. If COM object isn't support external memory management interface or no external memory manager is used in app it should use fallback ref count logic (internal memory management).


    • Edited by Muipo Thursday, September 22, 2016 11:33 AM
    Thursday, September 22, 2016 11:30 AM
  •    Found this post regarding native memory fragmentation - https://blogs.msdn.microsoft.com/ricom/2006/02/02/unmanaged-memory-fragmentation-an-old-story/. The post is very old and it is possible that this is a regular practice now. People employed double heap approach to resolve the performance issue associated with the memory fragmentation but I think the same approach might help resolve excessive memory consumption because of the fragmentation. I wonder if something like that is used in WinRT code?

    Alex

    Thursday, September 22, 2016 6:51 PM
  • Beautiful solution to native heap fragmentation problem :)
    Thursday, September 22, 2016 7:14 PM
  • When developing the Windows Store app platform (and then UWP), we realized that having cycles would be a huge adoption blocker for people writing .NET apps using WinRT API, especially with XAML objects. We worked with XAML team and developed a technology together so that XAML can inform the CLR GC with missing reference/edge information between CLR and XAML objects. And with that, GC is able to resolve cycles between CLR objects and XAML objects, with the complete graph information. I think this is in line with what you are seeing - if you are using CLR objects with XAML, you don't need to unsubscribe the events and there is no leak. 

    Maybe it would be benefical to all to make these mechanics publically available and standardized as an official public part of Windows Runtime/UWP platform?

    I have proposed above how traditional (and very ancient) COM model could be improved to help managed GC do its work. And as it's revealed now, UWP platform have some undocumented private mechanics to help managed GC in breaking ref count cycles!

    Traditional COM rely on internal memory management based on ref counting. But it wouldn't be much hard to introduce optional external memory management model for COM objects. COM object would implement optional COM interface to support external memory management (CLR Garbage Collector or C++ garbage collector as it's already available in C++ as an open source library, it may be anything as it would work via standardized COM interfaces). This COM interface (or set of COM interfaces) would report to external memory manager its memory consumption, references to other COM objects and other information GC require to do its work. If COM object is in external memory management mode (triggered by external memory manager) it would free memory not on reaching ref count = 0, but when external memory manager explicitly order it to do via external memory management interface. If COM object isn't support external memory management interface or no external memory manager is used in app it should use fallback ref count logic (internal memory management).


    I agree it would be very beneficial to have a common protocol for all parties to exchange such information. The challenge is that every language/runtime needs to participate, and it is not free. The guys in XAML team had to write a lot of code to report such information to CLR, and it is a very challenging problem to get it exactly right, in a multi-threaded environment, and make sure you are interacting with GC in the correct manner without introducing what we call "GC holes" (referencing a GC object that is not properly protected). It took us a while to flush out the bugs. 

    The existing contract itself is documented in MSDN (https://msdn.microsoft.com/en-us/library/windows/desktop/jj542495(v=vs.85).aspx), and has very similar idea with what you've proposed - there are just a lot more detailed involved. However, we don't feel that it is in a state that can be adopted by 3rd party - for example, it is not designed with multiple components in mind. And that is why we didn't broadcast it. We are still investigating what would be a better approach to handle this problem in a broader scope (such that any language/runtime can participate), and we'll share them when we are ready. 

    Thanks,

    Yi Zhang

    Thursday, September 22, 2016 8:39 PM
  • @Yi Zhang

    At least, memory profiling tools should use this protocol in a read-only (informative) manner. I have to say, that as for now, memory profiling of mixed managed/native UWP app is very confusing - see above. Visual Studio 2015 memory profiler doesn't provide convenient way to chase native memory leaks (if any) in managed app. As I described above, in my app it ends up with a situation when managed memory indicate no memory leaks and managed heap size is stable regardless of app usage, but native heap size and private working set keep growing for no obvious (for .NET developer, of course) reason. As I don't use any custom interop code and totally rely on built-in interop layer, it looks like serious problem on standard built-in interop side!

    Thursday, September 22, 2016 9:15 PM
  • Bump this thread
    Sunday, September 25, 2016 5:11 PM
  • Bump
    Tuesday, September 27, 2016 11:09 AM
  • Bump
    What for?
    Tuesday, September 27, 2016 11:15 AM
  • This topic isn't finished yet
    Tuesday, September 27, 2016 12:36 PM
  • >This topic isn't finished yet

    This is a long discussion with several different threads.  How do you think it should proceed?

    David


    David http://blogs.msdn.com/b/dbrowne/

    Tuesday, September 27, 2016 2:22 PM
  • >This topic isn't finished yet

    This is a long discussion with several different threads.  How do you think it should proceed?

    David


    David http://blogs.msdn.com/b/dbrowne/

    I haven't yet figured why this situation exactly is.

    What I exactly have observed is a really weird situation.

    When I analyze application with managed memory profiler it reports no memory leaks.

    Managed heap size is about 5Mb and don't growing much larger even after lots of navigation between pages and app usage. So, CLR managed memory profiler reports no leaks and managed heap analyze doesn't reveal any memory leaking. There was one source of managed memory leaks in CLR app - {x:Bind ...} (interesting, traditional runtime {Binding ...} don't leak at any circumstances, just change from {x:Bind ...} to {Binding ...} have automatically solved most managed memory leaks in app, but for better performance I have tuned {x:Bind ...} usage instead of fallback to traditional runtime bindings) compiled bindings, but these leaks was eliminated by some piece of manual memory management.

    So, no CLR managed memory leaks in app. But when I have observed native memory usage in process manager (private heap working set, total commit size, etc.), I have seen much different picture. Memory consumption was growing at danger rate and it quickly reached > 200Mb of private heap working set and keep growing after each page navigation - same time managed CLR heap size was changed predictably. If large amount of view model data was allocated (2-5Mb in size, I consider it is very large for application supposed to be used on ARM Windows 10 Mobile platform too), after navigation from such page that data was collected by GC. No weird things occurred. In the CLR managed heap world all was nice. But native memory usage (reported by process manager on PC and by process manager on device portal of Lumia 950 device) was awful reaching >200Mb and keep growing!

    When I profiled app in "Managed+Native mixed" mode, I have found lots of unallocated objects having weird mangled names and numbers instead of names. It is very hard and confusing to investigate anything in native heap. Natural logical conclusion is that leaks are occurring on COM side, not on CLR side. But as I didn't perform any custom COM interop, did not call any marshalling API and totally rely on built-in means to interop provided by .winmd for system framework APIs (native .winmd metadata, after all, provide enough information for compiler to do interop things automatically), I think there are some unnoticed issues in built-in managed/native interop mechanics.

    I was tested this situation using standard Visual Studio memory profiler (as far as I know only standard Visual Studio profiler can profile native heap and report native memory issues - no other known to me 3rd party profilers can do it for UWP apps). I was tested it in .NET Core Runtime build mode and in .NET Native build mode.

    Tuesday, September 27, 2016 2:38 PM
  • You were given the answer right from the beginning: the CLR holds references to the native world, thus preventing native objects from self destructing. Once the GC collects all the references, the objects self destruct.

    This is no real memory leak per see, it is just the GC taking its sweet time to collect stuff, because he does not know what these native references point to. This is fixed with XAML, as there is a custom framework implementation just for that (which explains why the sponsored background execution for media playback works the way it does).

    So you have to clear references manually, and you were given several methods to do so.

    Frankly, the only problem I see is the lack of official documentation on the matter.

    Tuesday, September 27, 2016 4:05 PM
  • You were given the answer right from the beginning: the CLR holds references to the native world, thus preventing native objects from self destructing. Once the GC collects all the references, the objects self destruct.

    I doubt it, as forced GC collect with WaitForPendingFinalizers doesn't work.

    Managed heap haven't references to XAML controls or pages (only current page and its controls are in managed heap) - so XAML pages/controls RCW wrappers are collected by GC properly and finalized, and in theory it would free native COM counted references. But native memory usage is still growing after page navigations and managed heap give no hints about why it is going this way.

    The only logical conclusion is: memory leak is occurring on COM side despite the fact that managed RCW wrappers was collected by GC and finalized.

    Another possibility is a heavy native heap fragmentation, but again - there is no out-of-the box diagnostic tools to test for it.



    • Edited by Muipo Tuesday, September 27, 2016 4:20 PM
    Tuesday, September 27, 2016 4:16 PM
  • >"The only logical conclusion is: memory leak is occurring on COM side despite the fact that managed RCW wrappers was collected by GC and finalized."

    It's always possible that there is a bug somewhere.  Can you share a repro?

    David


    David http://blogs.msdn.com/b/dbrowne/

    Tuesday, September 27, 2016 4:36 PM
  • It's problematic to compose repro as this issue is surfaced in the real-life complex application which use various 3rd-party open-source libraries including Win2D, Template10 and Managed ESENT.

    It may be issue in one of the 3rd-party libraries (Template10, for example), but it's hard to investigate.

    It would be much better if someone suggested proper diagnostic tools to chase this issue. Managed memory leaks are relatively easy to investigate as out-of-the box Visual Studio 2015 memory profiler is powerful enough for it and managed heap contains enough type information. But to investigate native memory leaks (if any?) is much like Dante's journey through Inferno, without Virgil's advice.




    • Edited by Muipo Tuesday, September 27, 2016 4:52 PM
    Tuesday, September 27, 2016 4:46 PM
  • Interesting discussion about Image.Source leaks.

    https://forums.xamarin.com/discussion/73907/image-source-leaks-memory-on-uwp-how-to-work-around

    I massively use small-to-medium sized images in app, mostly with Url image source (constructing URL pointing to internal file storage).

    Tuesday, September 27, 2016 5:42 PM
  • Some other interesting article about memory leaks with bitmap images in XAML

    http://blogs.developpeur.org/kookiz/archive/2013/02/17/wpdev-memory-leak-with-bitmapimage.aspx

    Tuesday, September 27, 2016 6:03 PM
  • It's problematic to compose repro as this issue is surfaced in the real-life complex application which use various 3rd-party open-source libraries including Win2D, Template10 and Managed ESENT.

    It may be issue in one of the 3rd-party libraries (Template10, for example), but it's hard to investigate.

    It would be much better if someone suggested proper diagnostic tools to chase this issue. Managed memory leaks are relatively easy to investigate as out-of-the box Visual Studio 2015 memory profiler is powerful enough for it and managed heap contains enough type information. But to investigate native memory leaks (if any?) is much like Dante's journey through Inferno, without Virgil's advice.




    Looks like you have done some investigation on your side and determined that there are no managed leaks. This is a great starting point. Diagnosing native memory leak is always a very very challenging problem. There are a few things you can try:

    1. Narrow down the problem in whatever way possible and reduce the problematic code path to a smaller (as possible) repro. The smaller repro, the more effective you are going to be during your future investigation, so it really pays off to have a repro as small as possible.

    2. Gflags/UMDH - this takes snapshot of the heap and you can diff between two snapshots and see which memory still remains. This article documents the steps: https://support.microsoft.com/en-us/kb/268343

    3. Adding logging code, data breakpoints, etc

    4. Repeat your scenario many times - the leaks will be most visible (than the non-leaks) if you repeat the scenario many times, especially it is a multiply of the number of iterations you have repeated. 

    5. Debug the code and watch for the memory allocation/free patterns

    6. Re-compile native code with debug C++ library with proper instrumentation. of course, this isn't always possible. 

    Unfortunately there isn't a silver bullet when it comes to diagnosing native leaks, and it takes a combination of different techniques/tools, lots of time/patience, good understanding of the full stack, and a little bit of luck. It is not uncommon to take multiple days/weeks/month to track down a native leak.

    You probably want to start by narrowing down the repro case first (before jumping straight into diagnosing native leaks using UMDH/etc), and probably start a new thread once you have a small enough repro case that demonstrate the problem. 

    Thanks,

    Yi Zhang


    Tuesday, September 27, 2016 11:57 PM
  • One major source of excessive native memory consumption is catched (need proper repro, although).

    It is a standard web browser control.

    Web browser on a page even if you navigate from app page (not web page!) doesn't free memory and each time adds up to +10 Mb of used native memory!

    When you leave from a page, memory is not freed and no GC helps.

    This XAML markup on page does leak native memory and does it badly:

                <WebView x:Name="ShowWeb">
                    
                </WebView>
    No event handlers are attached to web browser control.




    • Edited by Muipo Wednesday, September 28, 2016 2:15 AM
    Wednesday, September 28, 2016 1:55 AM
  • The easiest way to reproduce this issue is with this little code.

    int i =0;

    while(i<100000)

    {

         byte[] bytes = new byte[4096];

         var buffer = bytes.ToBuffer(); //<< magic line.

         i++;

    }

    Observe the memory leaking.

    Without calls to GC.Collect or GC.AddMemoryPressure, this will eventually crash with OOM exception on mobile phones.

    You can do roughly the exact same code in C++/CX, and you will see the native memory doesn't leak and self destructs accordingly, so the only problem is the GC.

    Another case study approach that was actually observed in production applications is implementing media stream sources in C#

    http://social.technet.microsoft.com/wiki/contents/articles/30827.implementing-media-stream-sources-in-windows-phone-runtime-for-background-audio-tasks.aspx

    I will see if I can still find some of my old C#-only media stream sources for the leak to be examined properly.

    • Edited by mcosmin Wednesday, September 28, 2016 10:25 AM
    Wednesday, September 28, 2016 10:22 AM
  • I have observed very interesting application behavior. I'm confused.

    1. App native memory is growing when managed memory isn't
    2. GC.Collect doesn't help
    3. Eventually native memory usage dropped by 50 Mb without visible reason!

    Above there was comments that GC.Collect will help. I have observed different behavior. I have invoked GC.Collect on every navigation between application pages for diagnostic reasons. It actually doesn't affected native memory consumption (as reported by Visual Studio 2015 diagnostic tools). But eventually (with no obvious reasons) native memory usage was significantly and dramatically dropped. For observed diagnostic case it was instantly dropped by 50 Mb! For this observation I have run app in diagnostic mode for more than 20 minutes.

    On stack overflow site I have read about similar observations - as UWP application may look "leaky" and GC.Collect doesn't help (in theory it should help as forced GC does collect and finalize Native/Managed RCW wrappers), but eventually and for no obvious reasons native memory consumption can dramatically drop.

    I have no rational explanations (due to lack of knowledge about native memory management in UWP apps) for this observed phenomena. As native memory management based on reference counting is deterministic, it's reasonable to think that if native memory leak and forced GC (releasing RCW wrappers) doesn't help, memory would never be released. It's a deterministic memory management model, after all!

    Looks like native heap (or XAML framework?) memory management is using nontrivial algorithms and re-uses memory chunks without actual deallocation? Or what?

    If nontrivial native memory management algorithms indeed are used - it should be explained on MSDN, as a lot of people who prefer to believe their eyes see supposedly "leaky" behavior in diagnostic tools and are thinking there are native and/or managed memory leaks for no obvious reasons.

    It's a "common knowledge" about Windows Runtime/UWP "memory leaks" - there are lot of discussions and threads about it and no one have exact answers on situation.

    It's because sane people prefer to build conclusions on facts, and most direct source of facts are Visual Studio 2015 performance diagnostic tool and Windows process manager.








    • Edited by Muipo Wednesday, September 28, 2016 11:05 AM
    Wednesday, September 28, 2016 10:38 AM
  • It is probably committed memory.

    if a process needs 50 MB at some point then drops to 30 MB, the OS will continue to commit 50MB for a while, to prevent memory fragmentation if the process ever needs 50MB again.This also improves performance if the process needs 50 MB ever again, and reuses the same area of memory to prevent expensive memory allocations (space already committed to cheaper to allocate).


    For example, in winRT background task hosts, the system automatically commits all maximum available memory, even though the process may use only a fraction of that, which makes memory allocations inside the background task really fast and fragmentation free from the OS level perspective.
    • Edited by mcosmin Wednesday, September 28, 2016 11:08 AM
    Wednesday, September 28, 2016 11:07 AM
  • @mcosmin

    Then it's probably a Visual Studio diagnostic tools issue. If diagnostic tool report native memory usage including "reserved" by OS memory space - these numbers and charts are misleading for memory leaks diagnostics.

    Diagnostic tools should also report actual allocated heap size (not including reserved memory space and not taking heap fragmentation into account). Or it looks like memory leaks if you just run app in diagnostic mode and look at provided by Visual Studio native memory consumption charts.



    • Edited by Muipo Wednesday, September 28, 2016 11:26 AM
    Wednesday, September 28, 2016 11:23 AM
  • >If diagnostic tool report native memory usage including "reserved" by OS memory space - these numbers

    Committed memory is not merely "reserved".  It's either backed by RAM or the page file, and takes up some of your system's "Commit Limit".  So it's extremely relevant diagnostically, since reaching your Commit Limit means "running out of memory", and getting close to it can cause excessive paging.

    See Reserving and Committing Memory

    And the point is that when you new-up an unmanaged object, the memory for that object typically comes from an already-committed region of the process' virtual address space (VAS).  Add when you free an unmanaged object, that VAS region is not  "decommitted". 

    >Diagnostic tools should also report actual allocated heap size

    Yes.  That's definitely key.  Imagine trying to diagnose CLR memory issues without visibility into the managed heap and its generations.   I have no idea what kind of allocation manager is in use in the unmanaged portion of your application, but that's the place to look at the memory behavior.

    David


    David http://blogs.msdn.com/b/dbrowne/

    Wednesday, September 28, 2016 12:03 PM
  • I have no idea what kind of allocation manager is in use in the unmanaged portion of your application, but that's the place to look at the memory behavior.

    No custom memory management indeed. App is totally relying on built-in UWP mechanics.

    It's:

    1. Standard XAML framework and WinRT built-in interop for vast majority of managed code
    2. Win2D 3rd-party library (developed in Visual C++ using standard Visual C++ runtime v14 libraries)
    3. 3rd party (actually, Microsoft's reference open-source implementation) ESENT managed wrapper (uses P/Invokes, as ESENT API isn't COM)

    It's all known to me native parts. There was custom native app part developed in C++/CX by me, but it's now obsolete and abandoned (still referenced in project, but unused and never invoked) as mature Win2D library provide all needed functionality out-of-the box.

    Appx package is depending on core CLR runtime package (.NET Native minimal runtime if compiled with .NET Native toolchain) and on Visual C++ runtime v14 package (because Win2D developed in C++)

    Wednesday, September 28, 2016 12:46 PM
  • Did you mean AsBuffer() (there is no method called ToBuffer, as far as I know).

    I'm using this code:

            private void button_Click(object sender, RoutedEventArgs e)
            {
                int i = 0;

                while (i < 10000000)
                {

                    byte[] bytes = new byte[4096];
                    var buffer = bytes.AsBuffer(); //<< magic line.

                    i++;
                }
            }

    And this is the memory diagnostic output - as you can see memory usage stays fairly consistent:

    I'm using Visual Studio 2015 Update 3, on Windows 10 Anniversary Update. 

    Is what you are seeing phone-specific?

    Wednesday, September 28, 2016 11:54 PM
  • Well Microsoft either fixed this leak between WP8.1 and W10M, or I need to give you a more complete sample to play with.
    Thursday, September 29, 2016 6:32 AM
  • I have investigated one confirmed source of memory leaks. Now, it's an obvious XAML framework bug (workaround is available).

    1. Create ListView with SemanticZoom and GridLayout on page using XAML
    2. Place ComboBox inside ListView.Header
    3. Add SelectionChanged event handler to ComboBox in XAML

    It would leak. If event handler to ComboBox is added in code and removed in page Unloaded event handler - it would not leak.

    Placing SelectionChanged event handler on ComboBox inside ListView header in XAML (thinking it would work as usual with event handler references placed in XAML - not require to remove it manually) would prevent page from being collected by GC. And root object holding entire object tree in memory would be SelectionChanged event handler!

    Thursday, September 29, 2016 8:16 AM
  • I have investigated one confirmed source of memory leaks. Now, it's an obvious XAML framework bug (workaround is available).

    1. Create ListView with SemanticZoom and GridLayout on page using XAML
    2. Place ComboBox inside ListView.Header
    3. Add SelectionChanged event handler to ComboBox in XAML

    It would leak. If event handler to ComboBox is added in code and removed in page Unloaded event handler - it would not leak.

    Placing SelectionChanged event handler on ComboBox inside ListView header in XAML (thinking it would work as usual with event handler references placed in XAML - not require to remove it manually) would prevent page from being collected by GC. And root object holding entire object tree in memory would be SelectionChanged event handler!

    Would you please share a small repro that demonstrate this problem? I'll be happy to take a look. If this is a XAML issue, I can pass this down to the folks in XAML team to investigate. It might also help if you can test your code in latest Windows 10 Anniversary Update, as I'm pretty sure XAML team has been making bug fixes in the past few releases that addresses issues like this.

    Thanks,

    Yi Zhang

    Thursday, September 29, 2016 6:29 PM
  • Hey guys,

    are here any news? now its almost 2 years later and the problem is still not fixed and nested in the core of UWP navigation...


    VB.NET &amp; C#.NET

    Friday, June 1, 2018 5:02 PM
  • @Nico F.  If you have problem, post a new question instead of replying to old threads.

    David


    Microsoft Technology Center - Dallas
    My blog

    Thursday, June 7, 2018 9:11 PM