【发布时间】:2021-03-04 09:29:31
【问题描述】:
更新:据可靠消息,微软正在调查此事。这些 API 调用无法正常工作,因此我尝试使用 std::filesystem 代替,但这不适用于“代理位置”的 UWP 应用程序。
仅通过查询文件系统,我就可以得到多达数百兆的泄漏。 考虑到 UWP 只允许在后台运行应用程序时使用 128MB 内存*,这似乎有点绝望。我认为这是内存泄漏是正确的还是有一些解释?下面显示的代码只是递归地获取 c:/program 文件的所有 StorageFile 对象(需要很长时间)。两个月前我已经尝试向微软报告(链接在末尾)。
*此信息似乎有误; UWP 内存限额显然因设备而异,在桌面上它似乎实际上是无限的。有一个函数 AppMemoryUsageLimit 可以查询这个。
c#/winrt 和 c++/winrt 示例如下;您可能必须进入 Windows 设置: 文件系统隐私设置 -> 允许应用访问您的文件系统
c#/winrt:
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();
}
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++/winrt:
#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();
printf("Count: %d", _count);
}
// The file-open stuff has gone out of scope now.
}
更多:c++/winrt 重复完成: 更新:c++/winrt 版本使用的内存(峰值使用量)是 c# 版本使用的 10 倍。我会问 c++/winrt 的人。
更新:我已经设法制作了一个 C# 版本,其中内存似乎已被回收,因此内存使用量只有一个峰值,而不是一个步骤: 再次注意,为了让这个应用程序正常工作,我必须在 Windows 中进行文件系统隐私设置:
【问题讨论】:
-
如果它一直持续到内存不足并崩溃,这就是泄漏。如果它运行到某个非破坏性点并停止,则可能只是延迟了垃圾收集。您是否从中遇到了实际问题,或者您只是注意到内存使用情况?
-
我认为它最终会崩溃。在 c# 中,托管内存保持低位,但本机内存变高。在 c++/winrt 中没有垃圾回收,但也可以看到非常高的内存消耗。
-
缓存实现表现出与真正的资源泄漏相同的资源消耗模式。您需要确保系统最终耗尽内存以区分它们。另请注意,C++ 确实 实现了垃圾收集,它只是确定性的。特别是对于 COM,它非常接近 .NET 的 CLR 实现的内容。有见地的阅读:Everybody thinks about garbage collection the wrong way.
-
如您所知,COM 是引用计数的。当一切都超出范围时,至少 C++ 版本应该释放内存,对吧?我会认为如果 COM 用完 1gig 来打开文件然后不释放该内存,那充其量是粗鲁的。
-
具有自动存储持续时间的 C++ 中的对象在超出范围时将被销毁。只要您保持基于价值的事物,这很容易遵循。这是 C++ 的本机语义模型。一旦您选择采用基于参考的设计(根据 COM 要求),事情就变得不那么明显了。无论如何,如果您想确定是否正在观察缓存的影响,只需用
file-open stuff写出两个连续的块,每个块都在自己的范围内。如果内存使用量翻倍,您就发现了潜在的资源泄漏。如果没有,您已经找到了缓存实现。