问题描述
这是一个简单的测试应用程序,可帮助您了解WPF内存使用情况.我想了解的关键问题是,为什么即使关闭了并等待GC完成后,为什么仍引用 MainWindow
而不释放它的内存?
This is a simple test application to help understand WPF memory usage. They key thing I want to understand is why MainWindow
is still referenced and it's memory not released, even after being closed and having waited for GC finalization?
(请参见下面的代码清单)
(See code listing below)
文本"MainWindow终结器"在快照2时尚未执行,这似乎是意外的.为了进行调查,我在代码清单中指示的位置使用VS诊断工具拍摄了两个内存快照.
The text "MainWindow finalizer" is not executed by the time of snapshot #2, which seems unexpected. To investigate, I have taken two memory snapshots using VS diagnostic tools at the points indicated in the code listing.
这是两个快照的VS比较:
Here's the VS comparison of the two snapshots:
这表明 MainWindow
仍然存在.但是为什么呢?向下钻取(再次使用诊断工具),结果毕竟是一个参考:
This shows that the MainWindow
is still around. But why, if nothing is referencing it? Drilling down (again using the diagnostic tools) it turns out that there is a reference after all:
还有其他对象也引用了 MainWindow
,但是它们最终都会形成一个循环,因此我认为它们并不是真正的根"对象.使参考保持生命的对象.但是对于 MediaContext
/ Dispatcher
组合,情况并非如此.
There are other objects also referencing the MainWindow
, but they all eventually form a cycle back to it, so I do not think they are genuinely "root" objects which are keeping the reference alive. But for the MediaContext
/ Dispatcher
duo this is not the case.
据我了解, Dispatcher
在每个线程中运行一次,因此单独运行就可以了.但是它拥有的 MediaContext
是怎么回事,而该代码又可以保存到我的 MainWindow
中?
The Dispatcher
as I understand it is run once per thread so that seems OK by itself. But what's up with the MediaContext
that it owns, which in turns holds onto my MainWindow
?
这正常吗?这是内存泄漏"吗?为什么会发生?
Is this normal? Is it a "memory leak"? Why does it happen?
此外,重要的是,我/应该如何摆脱 MainWindow
对象?
Also, importantly how can I / should I actually get rid of the MainWindow
object?
App.xaml:
<Application
x:Class="memtest.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:memtest"
StartupUri="MainWindow.xaml"
Startup="Application_Startup"
>
<Application.Resources/>
</Application>
App.xaml.cs:
namespace memtest
{
public partial class App : Application
{
private void Application_Startup(object sender, StartupEventArgs e)
{
// *** SNAPSHOT 1 ***
ShutdownMode = System.Windows.ShutdownMode.OnExplicitShutdown;
MainWindow window = new MainWindow();
window.Show();
window.Close();
window = null;
GC.Collect();
GC.WaitForPendingFinalizers();
GC.Collect();
// *** SNAPSHOT 2 ***
}
}
}
MainWindow.xaml.cs:
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
Debug.WriteLine("MainWindow constructor");
}
~MainWindow()
{
// Never reached
Debug.WriteLine("MainWindow finalizer");
}
}
MainWindow.XAML 是VS创建的默认设置,它仅包含一个空网格.
MainWindow.XAML is the default created by VS, which contains only an empty grid.
项目中没有其他代码.
这是一个.NET 4.72项目.
This is a .NET 4.72 project.
这不是> WPF窗口没有关闭后不会释放内存,因为它没有使用 WaitForPendingFinalizers()
,也没有使用显式的终结器.而且这个问题没有有效的答案.
This is not quite a dupe of A WPF window doesn't release the memory after closed, because it did not use WaitForPendingFinalizers()
nor did it use an explicit finalizer. And that question has no valid answers.
推荐答案
此测试犯了几个错误,也解释了此处
This test is making several mistakes, also explained here
- 您正在拍摄第二张快照,而MainWindow变量仍在堆栈框架上.JIT可以优化您对
window = null;
的分配,因为它可以清楚地看到以后不再使用该变量.此外,堆栈帧的GC报告不准确(相对于您的来源),堆栈上可能有隐藏的副本.将测试代码移到您要从其返回的单独方法中,以确保在堆栈上没有剩余对MainWindow的引用.(在确定下一个点后,从技术上讲是没有必要的,但是出于完整性的考虑,我会提到它,以便人们在编写GC测试时理解这一点.) - 您没有给多线程WPF渲染引擎足够的时间来清理,关闭和强制GC不足以与渲染引擎同步来清理其资源
- 您将在应用程序中保留
StartupUri =" MainWindow.xaml"
,将其删除以使使用固定代码的测试更加简单
- you are taking the second snapshot while the MainWindow variable is still on the stack frame. JIT is allowed to optimize your assignment to
window = null;
away because it can clearly see the variable is not used anymore afterwards. Furthermore GC reporting of stack frames is not exact (with respect to your source), there may be hidden copies on the stack. Move the test code into a separate method which you return from, to make sure no references to MainWindow are left on the stack. (Technically not necessary after fixing the next point, but I'm mentioning it for completeness so people understand that point when writing GC tests.) - you are not giving the multithreaded WPF rendering engine time to clean up, closing and forcing GC is not enough to synchronize with the rendering engine to clean up its resources
- you are leaving
StartupUri="MainWindow.xaml"
in the app, remove it to make testing with the fixed code simpler
执行测试的正确方法是启动DispatcherTimer并在那里获取第二张快照,对我来说MainWindow消失了.
The correct way to perform your test is to start a DispatcherTimer and take the second snapshot there, for me the MainWindow is gone then.
private void Application_Startup(object sender, StartupEventArgs e)
{
// *** SNAPSHOT 1 ***
ShutdownMode = System.Windows.ShutdownMode.OnExplicitShutdown;
RunTest();
GC.Collect();
GC.WaitForPendingFinalizers();
GC.Collect();
new DispatcherTimer(TimeSpan.FromSeconds(1), DispatcherPriority.Normal, Callback, Dispatcher).Start();
}
private void Callback(object sender, EventArgs e)
{
// *** SNAPSHOT 2 ***
}
private static void RunTest()
{
MainWindow window = new MainWindow();
window.Show();
window.Close();
window = null;
}
这篇关于即使关闭了GC Collect和WaitForPendingFinalizers,窗口对象在关闭后也不会释放?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!