WPF 窗口在程序终止之前不会释放其资源 [英] WPF window wont release its resources untill program terminates

查看:54
本文介绍了WPF 窗口在程序终止之前不会释放其资源的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我一直在阅读有关 WPF 内存处理的文章,并跟踪了前 5 名和前 8 名内存泄漏陷阱,但在我目前的情况下没有任何帮助.

I have been reading up on WPF memory handling and have followed every top 5 and top 8 Memory leak Pitfalls, but nothing helps me in my current situation.

我的软件有一个问题,WPF 在程序终止之前不会释放它的内存.如果我让它永远消失,无论我做什么都会导致 OutOfMemoryException.我设法在一个小样本中隔离了这个问题,以展示它是如何不释放内存的,即使我不再使用它.这是我重现问题的方法:

I have had an issue with my software where WPF won't release it memory until the program terminates. If I let it go forever it will cause an OutOfMemoryException no matter what I do. I have managed to isolate the issue within a small sample to show how it is not releasing its memory, even though I do not use it anymore. Here is how I can reproduce the problem:

我创建了 2 个项目,一个控制台程序和一个 WPF 应用程序.在我的 WPF 应用程序中,我有一个 MainWindow.xaml,其中没有任何内容:

I created 2 projects, one console program, and one WPF Application. In my WPF application I have a MainWindow.xaml which has nothing in it:

<Window x:Class="MemoryLeakWpfApp.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:MemoryLeakWpfApp"
        mc:Ignorable="d"
        Title="MainWindow" Height="450" Width="800" Loaded="MainWindow_OnLoaded">
    <Grid>

    </Grid>
</Window>

我确实订阅了用于立即关闭窗口的 Loaded 事件,该窗口可以在此处的 .cs 文件中看到:

I do subscribe to the Loaded event which I use to instantly close the window which can be seen in the .cs file here:

public partial class MainWindow : Window
{
    public MainWindow()
    {
        InitializeComponent();
        Debug.WriteLine("Constructing",GetType().Name);
    }

    ~MainWindow()
    {
        Debug.WriteLine("Deconstructing", GetType().Name);
    }

    private void MainWindow_OnLoaded(object sender, RoutedEventArgs e)
    {
        Close();
    }
}

我还在构造函数和解构函数中添加了调试行,以便我可以跟踪它何时被创建和丢弃.然后,我在 WPF 应用程序中创建一个 Controller 类,该类表示此 WPF 类库的入口点,该类库具有创建和显示窗口的方法:

I also have added debug lines to my constructor and deconstructor so I can track when it is created and discarded. I then create a Controller class in the WPF application which represents the entry point to this WPF class library that has a method to create and show the window:

public class Controller
{
    public void Execute()
    {
        MainWindow window = new MainWindow();
        window.ShowDialog();
        Debug.WriteLine("Constructing", GetType().Name);
    }

    ~Controller()
    {
        Debug.WriteLine("Deconstructing", GetType().Name);
    }
}

这里我还添加了调试跟踪线.我没有 App.xaml,因为此 WPF 项目在其属性中设置为类库.那就是 WPF 部分.在控制台项目中,我在主类中添加了以下代码:

Here I also added debug track lines. I don't have an App.xaml as this WPF project is set as a Class Library in its properties. That is the WPF Part. In the console project I added the following code to my main class:

[STAThread]
static void Main(string[] args)
{
    for (int i = 0; i < 100; i++)
    {
        Controller controller = new Controller();
        Console.WriteLine("Test " + i);
        controller.Execute();
    }

    Console.WriteLine("Pressing enter will close this");
    Console.ReadLine();
    Debug.WriteLine("Party is over, lets leave");
}

所以基本上设置是我有一个想要显示对话框的控制台类.它为 WPF 应用程序创建控制器并调用 Execute.控制器显示完成加载后立即关闭的窗口.然后控制台类创建一个新的控制器来重新执行这个过程.现在,这就是我在输出中看到的:

So basically the setup is that I have a console class that wants to show a dialog. It creates the controller for the WPF application and calls Execute. The controller show the window that immediately closes when it has done loading. The console class then creates a new controller to do the process all over again. Now, this is what I see in my output:

MainWindow: Constructing
Controller: Constructing
MainWindow: Constructing
Controller: Constructing
Controller: Deconstructing
MainWindow: Constructing
Controller: Constructing
Controller: Deconstructing
MainWindow: Constructing
Controller: Constructing
Controller: Deconstructing
MainWindow: Constructing
Controller: Constructing
Controller: Deconstructing

控制器正在构造和解构,但窗口不是.但是,当 for 循环完成并按 Enter 让程序运行时,我得到了:

The controller is constructing and deconstructing, but the window is not. However, when the for loop is complete and I press enter to let the program run out, I get this:

Party is over, lets leave
MainWindow: Deconstructing
Controller: Deconstructing
MainWindow: Deconstructing
Controller: Deconstructing
MainWindow: Deconstructing
MainWindow: Deconstructing
MainWindow: Deconstructing
MainWindow: Deconstructing
MainWindow: Deconstructing
MainWindow: Deconstructing
MainWindow: Deconstructing
MainWindow: Deconstructing
MainWindow: Deconstructing
MainWindow: Deconstructing
MainWindow: Deconstructing

突然间,MainWindow 的所有实例现在都在解构,但仅在程序运行时,而不是在我们丢弃 for 循环中的引用时.这意味着在我们的程序中,在 OutOfMemoryException 发生之前,我们只有有限的次数可以打开窗口.

Suddenly all the instances of the MainWindow are now deconstructing, but only when the program runs out, not when we discard the reference in the for loop. This means that in our program we only have a finite number of times we can open the window before an OutOfMemoryException will occur.

但是百万半美元的问题是:如何说服 WPF 在程序运行时而不是在程序关闭时释放其内存?

But the million and a half dollar question is: How can I persuade WPF to release its memory while the program is running and not when the program closes?

推荐答案

您声称自己是 [STAThread] 但您没有消息泵.没有消息泵,您就不是真正的 STA.在这种特殊情况下,这意味着 WPF 永远没有机会清理其资源.WPF 可能正在将消息发布到消息队列,而这些消息从未被接收.

You claim being an [STAThread] yet you have no message pump. Without a message pump you are not truely STA. In this particular case this means WPF never gets the chance to clean up its resources. WPF is probably posting messages to the message queue which are never picked up.

由于 WPF 是一个多线程系统,它必须执行后台操作,包括在多个线程之间进行同步.为了返回主线程,它使用了 Dispatcher 基础设施,您没有正确设置.

Since WPF is a multithreaded system it has to perform background operations, including synchronizing between multiple threads. To get back to the main thread it uses the Dispatcher infrastructure, which you have not setup correctly.

要解决您的问题,您需要在 STA 线程上运行 WPF Dispatcher,而不是实现您自己的循环.

To fix your problem you need to be running a WPF Dispatcher on the STA thread, not implementing your own loop.

此外,为了完整起见,链接到 相关帖子 把我带到了这里.确保在设置调度程序基础架构后衡量正确的内容.

Also, for completeness, link to a related post which brought me here. Make sure you measure the right thing after setting up the dispatcher infrastructure.

这篇关于WPF 窗口在程序终止之前不会释放其资源的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆