[UWP][C++/WINRT] Massive memory leaks?
Revised question:
Do UWP file operations have memory leaks? Below I have the C++/winrt and c#/winrt versions of an example where we get the StorageFile objects of c:/program files (recursive). This takes a long time to run, and there appears to be a large memory leak amounting to tens of megabytes or more on my machine.
c# version:
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices.WindowsRuntime;
using Windows.Foundation;
using Windows.Foundation.Collections;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Controls.Primitives;
using Windows.UI.Xaml.Data;
using Windows.UI.Xaml.Input;
using Windows.UI.Xaml.Media;
using Windows.UI.Xaml.Navigation;
using System.Diagnostics;
// The Blank Page item template is documented at https://go.microsoft.com/fwlink/?LinkId=402352&clcid=0x409
namespace MemoryLeak001_CSharp_2
{
/// <summary>
/// An empty page that can be used on its own or navigated to within a Frame.
/// </summary>
public sealed partial class MainPage : Page
{
public MainPage()
{
this.InitializeComponent();
}
// Can take a very long time
private async void Button_Click(object sender, RoutedEventArgs e)
{
GC.Collect();
Trace.WriteLine("\nButton_Click started...\n");
{
Windows.Storage.StorageFolder _root = await Windows.Storage.StorageFolder.GetFolderFromPathAsync("C:\\program files");
var _storageFiles = await _root.GetFilesAsync(Windows.Storage.Search.CommonFileQuery.OrderByName);
var _count = _storageFiles.Count;
}
GC.Collect();
GC.WaitForPendingFinalizers();
Trace.WriteLine("\nButton_Click finished.\n");
}
}
}
c
c++ version:
#include "pch.h"
#include <thread>
#include <winrt/Windows.Storage.h>
#include <winrt/Windows.Storage.Streams.h>
#include <winrt/Windows.Foundation.Collections.h>
#include <winrt/Windows.Storage.Search.h>
using namespace winrt;
using namespace Windows::Foundation;
using namespace winrt::Windows::Storage::Streams;
using namespace winrt::Windows::Storage::Search;
int main()
{
init_apartment();
__int64 _count{ -1 };
{
winrt::Windows::Storage::StorageFolder _root = winrt::Windows::Storage::StorageFolder::GetFolderFromPathAsync(L"c:\\program files\\").get();
auto _storageFiles = _root.GetFilesAsync(CommonFileQuery::OrderByName).get();
_count = _storageFiles.Size();
}
// The file-open stuff has gone out of scope now.
//Uri uri(L"http://aka.ms/cppwinrt");
printf("Count: %d", _count);
}
----------
Old version of question:
With the async functions, such as for opening files, I have experimented with using co_await, .Completed(...) and also .get() to do the same things.̶F̶o̶r̶ ̶.̶g̶e̶t̶(̶)̶ ̶I̶ ̶t̶h̶i̶n̶k̶ ̶I̶ ̶a̶m̶ ̶g̶e̶t̶t̶i̶n̶g̶ ̶q̶u̶i̶t̶e̶ ̶a̶ ̶l̶o̶t̶ ̶o̶f̶ ̶m̶e̶m̶o̶r̶y̶ ̶l̶e̶a̶k̶.̶ Update: I don't think it's just get(); seems to be file operations in general. In the example below I open a large file (~1.5GB). There seems to be a residual of heap memory usage, amounting to over 300kB in this case. Am I wrong in thinking that this is a memory leak?
#include "pch.h"
#include <thread>
#include <winrt/Windows.Storage.h>
#include <winrt/Windows.Storage.Streams.h>
using namespace winrt;
using namespace Windows::Foundation;
int main()
{
init_apartment();
{
winrt::Windows::Storage::StorageFile _storageFile = winrt::Windows::Storage::StorageFile::GetFileFromPathAsync(L"C:\\The Crush (1993) [1080p] [YTS.AG]\\The.Crush.1993.1080p.BluRay.x264-[YTS.AG].mp4").get();
using namespace winrt::Windows::Storage::Streams;
IRandomAccessStreamWithContentType _stream = _storageFile.OpenReadAsync().get();
const size_t _fileSize = _stream.Size();
std::vector<uint8_t>_buffer(_fileSize);
using namespace Windows::Storage::Streams;
const IInputStream _inputStream = _stream.GetInputStreamAt(0);
const IDataReader _dataReader = winrt::Windows::Storage::Streams::DataReader(_inputStream);
const uint32_t _loadTask = _dataReader.LoadAsync(_fileSize).get();
_dataReader.ReadBytes(_buffer);
}
// The file-open stuff has gone out of scope now.
Uri uri(L"http://aka.ms/cppwinrt");
printf("Hello, %ls!\n", uri.AbsoluteUri().c_str());
while (true)
{
std::this_thread::sleep_for(std::chrono::milliseconds(20));
}
}
UPDATE:
Another example:
#include "pch.h"
#include <thread>
#include <winrt/Windows.Storage.h>
#include <winrt/Windows.Storage.Streams.h>
#include <winrt/Windows.Foundation.Collections.h>
#include <winrt/Windows.Storage.Search.h>
using namespace winrt;
using namespace Windows::Foundation;
using namespace winrt::Windows::Storage::Streams;
using namespace winrt::Windows::Storage::Search;
int main()
{
init_apartment();
{
winrt::Windows::Storage::StorageFolder _root = winrt::Windows::Storage::StorageFolder::GetFolderFromPathAsync(L"c:\\program files\\").get();
auto _storageFiles = _root.GetFilesAsync(CommonFileQuery::OrderByName).get();
}
// The file-open stuff has gone out of scope now.
Uri uri(L"http://aka.ms/cppwinrt");
printf("Hello, %ls!\n", uri.AbsoluteUri().c_str());
}
The memory usage peaks at 1.2 gig for me, and quite a lot is leaked afterwards as well. I think the peak worked out at 40k per StorageFile. I tried using umdh to shed some light on this. Here are some of the biggest residuals:
- 3d400 ( 3d400 - 0) 38 allocs BackTrace37D71046
-
38 ( 38 - 0) BackTrace37D71046 allocations
ntdll!RtlpCallInterceptRoutine+3F
ntdll!RtlpAllocateHeapInternal+78B6B
combase!CInternalPageAllocator::Grow+3B (onecore\com\combase\coll\pgalloc.cxx, 348)
combase!CPageAllocator::AllocEntry+AD (onecore\com\combase\coll\pgalloc.cxx, 637)
combase!CGIPTable::RegisterInterfaceInGlobalHlp+130 (onecore\com\combase\dcomrem\giptbl.cxx, 826)
combase!CGIPTable::RegisterInterfaceInGlobal+14 (onecore\com\combase\dcomrem\giptbl.cxx, 762)
windows.storage!CFreeThreadedItemContainer::Initialize+F3
windows.storage!CFSFolder::_BindToChild+347
windows.storage!CFSFolder::_Bind+A08
windows.storage!CFSFolder::BindToObject+40A
Windows.Storage.Search!CGrepQuery::_EvaluateItem+153
Windows.Storage.Search!CGrepQuery::CrawlForNextItemImpl+116
Windows.Storage.Search!TestHook_GrepQuery_CrawlForNextItem+1D
Windows.Storage.Search!CGrepQuery::CrawlForNextItem+1D
Windows.Storage.Search!CGrepRowset::GetRowsAt+59
Windows.Storage.Search!CQueryResultSet::FetchResultAt+E1
Windows.Storage.Search!CRowsetEnumeration::_EnumerateRowset+10F
Windows.Storage.Search!CRowsetEnumeration::WaitForResults+4D
Windows.Storage.Search!CQueryResultSet::_HandleBatches+7B
Windows.Storage.Search!CQueryResultSet::RealizeResults+252
windows.storage!CRealizeTask::_DoRealization+81
windows.storage!<lambda_a7b5b9f2e371967015d74b1e2c6d4552>::operator()+76
shcore!WorkThreadManager::CThread::ThreadProc+260
shcore!WorkThreadManager::CThread::s_ExecuteThreadProc+18
shcore!<lambda_142c425290ac4fbd4d5aee2fc3f7d711>::<lambda_invoker_cdecl>+23
ntdll!TppSimplepExecuteCallback+123
ntdll!TppWorkerThread+8D4
KERNEL32!BaseThreadInitThunk+14
ntdll!RtlUserThreadStart+21
- 5a90 ( 5a90 - 0) 12 allocs BackTrace3733FFC6
-
12 ( 12 - 0) BackTrace3733FFC6 allocations
ntdll!RtlpCallInterceptRoutine+3F
ntdll!RtlpAllocateHeapInternal+78B6B
windows.storage!operator new+12
windows.storage!CTopViewDescription_Create+3F
windows.storage!CFolderTypeDescription::_LoadTopViews+199
windows.storage!CFolderTypeDescription::_LoadFolderTypeInfo+2FA
windows.storage!CFolderTypeDescription::CFolderTypeInfoResourceGuard::v_LoadResource+61
windows.storage!CSRWLockResourceGuardBase::ExecuteReader<<lambda_c4ea1647e09c272de9f28c49495bae53> >+C2
windows.storage!CFolderTypeDescription::GetTopViewByID+56
windows.storage!SHGetTopViewDescription+68
windows.storage!CQueryOptions::_InitMembersFromTopViewInBroker+AA
windows.storage!Microsoft::WRL::Details::MakeAndInitialize<CQueryOptions,CQueryOptions,_GUID>+79
windows.storage!CStorageFolderStaticsBrokered::GetTopViewQueryOptions_Private+50
RPCRT4!Invoke+73
RPCRT4!Ndr64StubWorker+B56
RPCRT4!NdrStubCall3+C9
combase!CStdStubBuffer_Invoke+5F (onecore\com\combase\ndr\ndrole\stub.cxx, 1524)
RPCRT4!CStdStubBuffer_Invoke+3B
combase!ObjectMethodExceptionHandlingAction<<lambda_c9f3956a20c9da92a64affc24fdd69ec> >+43 (onecore\com\combase\dcomrem\excepn.hxx, 87)
combase!DefaultStubInvoke+1C3 (onecore\com\combase\dcomrem\channelb.cxx, 1452)
combase!SyncServerCall::StubInvoke+26 (onecore\com\combase\dcomrem\servercall.hpp, 826)
combase!ServerCall::ContextInvoke+42A (onecore\com\combase\dcomrem\ctxchnl.cxx, 1418)
combase!ReentrantSTAInvokeInApartment+19D (onecore\com\combase\dcomrem\reentrantsta.cpp, 112)
combase!AppInvoke+1EC (onecore\com\combase\dcomrem\channelb.cxx, 1182)
combase!ComInvokeWithLockAndIPID+681 (onecore\com\combase\dcomrem\channelb.cxx, 2290)
combase!ThreadWndProc+3AD (onecore\com\combase\dcomrem\chancont.cxx, 744)
USER32!UserCallWinProcCheckWow+2BD
USER32!DispatchMessageWorker+1E2
shcore!WorkThreadManager::CThread::ThreadProc+8CC
shcore!WorkThreadManager::CThread::s_ExecuteThreadProc+18
shcore!<lambda_9844335fc14345151eefcc3593dd6895>::<lambda_invoker_cdecl>+11
KERNEL32!BaseThreadInitThunk+14
- 3c80 ( 3c80 - 0) 8 allocs BackTrace34EFBA86
-
8 ( 8 - 0) BackTrace34EFBA86 allocations
ntdll!RtlpCallInterceptRoutine+3F
ntdll!RtlpAllocateHeapInternal+78B6B
ucrtbase!_calloc_base+4E
ucrtbase!DllMainDispatch+125
ntdll!LdrpCallInitRoutine+65
ntdll!LdrpInitializeThread+15A
ntdll!_LdrpInitialize+89
ntdll!LdrpInitialize+3B
ntdll!LdrInitializeThunk+E
- 3842 ( 3842 - 0) 12 allocs BackTrace37342006
-
12 ( 12 - 0) BackTrace37342006 allocations
ntdll!RtlpCallInterceptRoutine+3F
ntdll!RtlpAllocateHeapInternal+78B6B
shcore!SHStrDupW+4B
propsys!PSPropertyBag_ReadStrAlloc+2F
windows.storage!CTopViewDescription::Initialize+2AB
windows.storage!CTopViewDescription_Create+68
windows.storage!CFolderTypeDescription::_LoadTopViews+199
windows.storage!CFolderTypeDescription::_LoadFolderTypeInfo+2FA
windows.storage!CFolderTypeDescription::CFolderTypeInfoResourceGuard::v_LoadResource+61
windows.storage!CSRWLockResourceGuardBase::ExecuteReader<<lambda_c4ea1647e09c272de9f28c49495bae53> >+C2
windows.storage!CFolderTypeDescription::GetTopViewByID+56
windows.storage!SHGetTopViewDescription+68
windows.storage!CQueryOptions::_InitMembersFromTopViewInBroker+AA
windows.storage!Microsoft::WRL::Details::MakeAndInitialize<CQueryOptions,CQueryOptions,_GUID>+79
windows.storage!CStorageFolderStaticsBrokered::GetTopViewQueryOptions_Private+50
RPCRT4!Invoke+73
RPCRT4!Ndr64StubWorker+B56
RPCRT4!NdrStubCall3+C9
combase!CStdStubBuffer_Invoke+5F (onecore\com\combase\ndr\ndrole\stub.cxx, 1524)
RPCRT4!CStdStubBuffer_Invoke+3B
combase!ObjectMethodExceptionHandlingAction<<lambda_c9f3956a20c9da92a64affc24fdd69ec> >+43 (onecore\com\combase\dcomrem\excepn.hxx, 87)
combase!DefaultStubInvoke+1C3 (onecore\com\combase\dcomrem\channelb.cxx, 1452)
combase!SyncServerCall::StubInvoke+26 (onecore\com\combase\dcomrem\servercall.hpp, 826)
combase!ServerCall::ContextInvoke+42A (onecore\com\combase\dcomrem\ctxchnl.cxx, 1418)
combase!ReentrantSTAInvokeInApartment+19D (onecore\com\combase\dcomrem\reentrantsta.cpp, 112)
combase!AppInvoke+1EC (onecore\com\combase\dcomrem\channelb.cxx, 1182)
combase!ComInvokeWithLockAndIPID+681 (onecore\com\combase\dcomrem\channelb.cxx, 2290)
combase!ThreadWndProc+3AD (onecore\com\combase\dcomrem\chancont.cxx, 744)
USER32!UserCallWinProcCheckWow+2BD
USER32!DispatchMessageWorker+1E2
shcore!WorkThreadManager::CThread::ThreadProc+8CC
shcore!WorkThreadManager::CThread::s_ExecuteThreadProc+18
- 2f4e ( 2f4e - 0) 78 allocs BackTrace37757A46
-
78 ( 78 - 0) BackTrace37757A46 allocations
ntdll!RtlpCallInterceptRoutine+3F
ntdll!RtlpAllocateHeapInternal+78B6B
KERNELBASE!LocalAlloc+6D
windows.storage!CByteHashTable::_AddUpdateItem+E9
windows.storage!_LookupForSet+BE
windows.storage!_SetFileClassInt+40
windows.storage!CFileExtension::EnsureClassFlags+20F
windows.storage!CFileSysItemString::GetJunctionClsid+7C
windows.storage!CFSFolder::_ItemHasNavigationEnum+4C
windows.storage!CFSFolder::_CreateIDList+839
windows.storage!CFileSysEnum::CreateIDList+62
windows.storage!CFileSysEnum::Next+102
Windows.Storage.Search!CGrepQuery::CrawlForNextItemImpl+A7
Windows.Storage.Search!TestHook_GrepQuery_CrawlForNextItem+1D
Windows.Storage.Search!CGrepQuery::CrawlForNextItem+1D
Windows.Storage.Search!CGrepRowset::GetRowsAt+59
Windows.Storage.Search!CQueryResultSet::FetchResultAt+E1
Windows.Storage.Search!CRowsetEnumeration::_EnumerateRowset+10F
Windows.Storage.Search!CRowsetEnumeration::WaitForResults+4D
Windows.Storage.Search!CQueryResultSet::_HandleBatches+7B
Windows.Storage.Search!CQueryResultSet::RealizeResults+252
windows.storage!CRealizeTask::_DoRealization+81
windows.storage!<lambda_a7b5b9f2e371967015d74b1e2c6d4552>::operator()+76
shcore!WorkThreadManager::CThread::ThreadProc+260
shcore!WorkThreadManager::CThread::s_ExecuteThreadProc+18
shcore!<lambda_142c425290ac4fbd4d5aee2fc3f7d711>::<lambda_invoker_cdecl>+23
ntdll!TppSimplepExecuteCallback+123
ntdll!TppWorkerThread+8D4
KERNEL32!BaseThreadInitThunk+14
ntdll!RtlUserThreadStart+21