【问题标题】:UWP file operations give memory leaks?UWP 文件操作会导致内存泄漏?
【发布时间】: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.       
}

https://docs.microsoft.com/en-us/answers/questions/112585/uwpcwinrt-get-causing-memory-leaks-with-async-oper.html

更多: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 写出两个连续的块,每个块都在自己的范围内。如果内存使用量翻倍,您就发现了潜在的资源泄漏。如果没有,您已经找到了缓存实现。

标签: c# uwp c++-winrt


【解决方案1】:

这似乎是 C++/WinRT 版本使用 10 倍于 C# 版本的内存的预期行为。

相对于 C#,C++ 内存使用量增加,.NET 可以更长时间地缓存 C++ COM 对象,因为它们是创建和删除更昂贵的对象。这可能意味着一旦一个对象被取消引用,它可能仍然保存在内存中,因为 GC 还没有机会运行,一旦它运行,它将运行 CoFreeUnusedLibrariesEx() 调用,然后继续调用 DllCanUnloadNow() .

当应用程序运行时,C++ COM 对象可能会占用更多内存,但是一旦 GC 运行或应用程序内的条件需要清理时,分配的资源最终将被释放。 (基于当前内存使用情况、可用内存、性能和其他因素。)

垃圾收集器最终将根据各种因素运行,包括应用程序性能和剩余可用内存总量。如果应用程序一直运行直到它因 OutOfMemory 异常而崩溃,那么这可能表明存在泄漏,因为垃圾收集器没有按预期释放资源,并且无法将更多资源分配到内存中。

【讨论】:

  • 你能告诉我你关于使用 std::filesystem 的问题已经解决了吗?
  • std::filesystem 仍然无法在 UWP 应用中运行。 STL 人员认为这“不是标准库问题”。我设法让一个 c# 示例正常工作 - 内存峰值而不是一步;请参阅我添加到问题末尾的图片。我不使用 c#;我使用 c++/winrt,但也许这最后一件事可以提供线索。这个文件操作内存问题很糟糕; UWP 无法打开大量文件。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2021-03-23
  • 2021-09-25
  • 2014-12-08
  • 1970-01-01
  • 1970-01-01
  • 2010-09-09
相关资源
最近更新 更多