microsoft / microsoft-ui-xaml

Windows UI Library: the latest Windows 10 native controls and Fluent styles for your applications
MIT License
6.36k stars 678 forks source link

Memory leaks in unpackaged C++-Desktop-App #9534

Open Woopsalla opened 7 months ago

Woopsalla commented 7 months ago

Describe the bug

I created an unpackaged desktop app with the VS template "Blank App, Packaged (WinUI 3 in Desktop)" and only added some code (_CrtSetDbgFlag etc.) to detect memory leaks. When the app is started and stopped again with VS, memory leaks are shown. Then I added some code that draws something in a canvas again and again to demonstrate rapidly growing storage requirements when an app has to update drawn content periodically.

Steps to reproduce the bug

1) Download the following app and build the debug version: UnpackagedDesktopApp_MemLeak_SDK1_5_1.zip 2) Start and stop the app in the Visual Studio without clicking the demo button. 3) Check the memory leaks in the output window of the VS. 4) Open the task manager to check memory requirements of the app. 5) Start the app again and click the button. 6) Monitor the growing memory requirements in the task manager. ...

Expected behavior

No memory leaks. No growing memory requirements due to updated drawings.

Screenshots

No response

NuGet package version

Windows App SDK 1.5.1: 1.5.240311000

Packaging type

Unpackaged

Windows version

Windows 10 version 22H2 (19045, 2022 Update)

IDE

Visual Studio 2022

Additional context

No response

DarranRowe commented 7 months ago

For the first leak you mention here, it is a harmless leak. When you create an instance of Application, via App, the runtime stashes an extra reference to Application somewhere. This is never released when the process exits. You can verify this by defining a destructor in App and placing a breakpoint in it, the breakpoint is never hit. If you do some nasty things and release that final reference to App, the leaks almost completely go away. For example, if I override the Xaml defined entry point function and use the following:

#include "pch.h"
#include "App.xaml.h"

void nasty_stuff_that_frees_the_application(const winrt::Microsoft::UI::Xaml::Application &app);
void check_process_requirements();

int APIENTRY wWinMain(_In_ HINSTANCE, _In_opt_ HINSTANCE, _In_ LPWSTR, _In_ int)
{
    check_process_requirements();

    auto flags = _CrtSetDbgFlag(_CRTDBG_REPORT_FLAG);
    flags = (flags & 0x0000ffff) | _CRTDBG_LEAK_CHECK_DF;
    _CrtSetDbgFlag(flags);

    winrt::Microsoft::UI::Xaml::Application app = nullptr;

    winrt::Microsoft::UI::Xaml::Application::Start(
        [&app](auto &&)
        {
            app = winrt::make<winrt::Meh::implementation::App>();
        });

    nasty_stuff_that_frees_the_application(app);
    app = nullptr;

    return 0;
}

The following is the left over detected blocks of memory left allocated:

Detected memory leaks!
Dumping objects ->
{406} normal block at 0x000002D3241429D0, 32 bytes long.
 Data: <0 E       E     > 30 86 45 00 F6 7F 00 00 00 86 45 00 F6 7F 00 00 
{272} normal block at 0x000002D324142E50, 24 bytes long.
 Data: < -              > 18 2D 07 17 FF 7F 00 00 01 00 00 00 00 00 00 00 
Object dump complete.
The program '[15928] Meh.exe' has exited with code 0 (0x0).

I'm not going into how I released that last reference to Application because it is guaranteed to leave a dangling pointer somewhere in the Xaml runtime. While it doesn't cause problems now for short periods after it is deleted, there is no guarantee that there will never be a use after free in the future. If you want to work it out yourself, it is basically obtaining the ABI interface and releasing it without an AddRef or QI. But this is certainly not something that you should do, I'm only doing it here to illustrate a point.

The remaining two blocks could be related to the dispatcher queue, which I don't believe gets shut down either. But these are harmless because the amount of blocks does not grow and Windows will reclaim the memory when the process' heap gets destroyed when the process exits. However, I understand that these leaks can get in the way of debugging.

Woopsalla commented 7 months ago

Thank you, Darran, for clarifying the facts. That at least explains that part. And yes, of course this makes troubleshooting more difficult and should definitely be fixed. However, the second problem pointed out by the application is much more problematic (see points 5 and 6). The constantly growing memory requirements of the application will paralyse the system in a relatively short time. For our actual application, the memory problem is even worse, as it has to update an extensive GUI at very short intervals. This is currently preventing us from delivering the product and is absolutely critical. It may not be a memory leak in the classic sense, but only in the sense that the memory is apparently still being released correctly before the app is exited. But not as long as the app is running. So it's just a question of timing. Of course, it is important that this problem is fixed, because it is serious. For the moment, however, an explanation would help me, and above all a workaround.

Woopsalla commented 7 months ago

In my opinion, an application that generates its own graphical elements and updates their display regularly is a standard use case. If such applications cause the user systems to crash, this is highly critical. Accordingly, this problem should also be classified here! Is there anyone in the Microsoft team who can help with this?