Window 对象在关闭后未释放,即使在 GC Collect 和 WaitForPendingFinalizers 之后?
Window object not released after being closed, even after GC Collect and WaitForPendingFinalizers?
这是一个简单的测试应用程序,可帮助了解 WPF 内存使用情况。我想了解的关键是为什么 MainWindow
仍然被引用并且它的内存没有释放,即使在关闭并等待 GC 完成后也是如此?
(见下面的代码清单)
文本“MainWindow finalizer”在快照 #2 时并未执行,这似乎出乎意料。为了进行调查,我使用 VS 诊断工具在代码清单中指示的点拍摄了两个内存快照。
下面是两张快照的VS对比:
这表明 MainWindow
仍然存在。但是,如果没有任何内容引用它,为什么呢?向下钻取(再次使用诊断工具)发现毕竟有一个参考:
还有其他对象也引用了 MainWindow
,但它们最终都形成了一个循环回到它,所以我不认为它们是真正使引用保持活动状态的“根”对象。但对于 MediaContext
/ Dispatcher
二人组来说,情况并非如此。
据我所知,Dispatcher
是每个线程一次 运行,因此它本身似乎没问题。但是它拥有的 MediaContext
怎么了,它又持有我的 MainWindow
?
这正常吗?是“内存泄漏”吗?为什么会这样?
此外,重要的是我如何/我应该如何真正摆脱 MainWindow
对象?
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默认创建的,里面只包含一个空的grid。
项目中没有其他代码。
这是一个 .NET 4.72 项目。
这不完全是 A WPF window doesn't release the memory after closed 的骗局,因为它没有使用 WaitForPendingFinalizers()
也没有使用显式终结器。这个问题没有有效答案。
这个测试有几个错误,也解释了here
- 您正在拍摄第二张快照,而 MainWindow 变量仍在堆栈帧上。允许 JIT 优化您对
window = null;
的分配,因为它可以清楚地看到该变量之后不再使用。此外,堆栈帧的 GC 报告并不准确(相对于您的来源),堆栈上可能存在隐藏的副本。将测试代码移动到您 return 所在的单独方法中,以确保没有对 MainWindow 的引用留在堆栈中。 (在修复下一点后技术上没有必要,但我提到它是为了完整性,以便人们在编写 GC 测试时理解这一点。)
- 您没有给多线程 WPF 渲染引擎清理的时间,关闭和强制 GC 不足以与渲染引擎同步以清理其资源
- 您要将
StartupUri="MainWindow.xaml"
留在应用程序中,请将其删除以简化使用固定代码的测试
执行测试的正确方法是启动一个 DispatcherTimer 并在那里拍摄第二张快照,对我来说,MainWindow 已经消失了。
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;
}
这是一个简单的测试应用程序,可帮助了解 WPF 内存使用情况。我想了解的关键是为什么 MainWindow
仍然被引用并且它的内存没有释放,即使在关闭并等待 GC 完成后也是如此?
(见下面的代码清单)
文本“MainWindow finalizer”在快照 #2 时并未执行,这似乎出乎意料。为了进行调查,我使用 VS 诊断工具在代码清单中指示的点拍摄了两个内存快照。
下面是两张快照的VS对比:
这表明 MainWindow
仍然存在。但是,如果没有任何内容引用它,为什么呢?向下钻取(再次使用诊断工具)发现毕竟有一个参考:
还有其他对象也引用了 MainWindow
,但它们最终都形成了一个循环回到它,所以我不认为它们是真正使引用保持活动状态的“根”对象。但对于 MediaContext
/ Dispatcher
二人组来说,情况并非如此。
据我所知,Dispatcher
是每个线程一次 运行,因此它本身似乎没问题。但是它拥有的 MediaContext
怎么了,它又持有我的 MainWindow
?
这正常吗?是“内存泄漏”吗?为什么会这样?
此外,重要的是我如何/我应该如何真正摆脱 MainWindow
对象?
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默认创建的,里面只包含一个空的grid。
项目中没有其他代码。
这是一个 .NET 4.72 项目。
这不完全是 A WPF window doesn't release the memory after closed 的骗局,因为它没有使用 WaitForPendingFinalizers()
也没有使用显式终结器。这个问题没有有效答案。
这个测试有几个错误,也解释了here
- 您正在拍摄第二张快照,而 MainWindow 变量仍在堆栈帧上。允许 JIT 优化您对
window = null;
的分配,因为它可以清楚地看到该变量之后不再使用。此外,堆栈帧的 GC 报告并不准确(相对于您的来源),堆栈上可能存在隐藏的副本。将测试代码移动到您 return 所在的单独方法中,以确保没有对 MainWindow 的引用留在堆栈中。 (在修复下一点后技术上没有必要,但我提到它是为了完整性,以便人们在编写 GC 测试时理解这一点。) - 您没有给多线程 WPF 渲染引擎清理的时间,关闭和强制 GC 不足以与渲染引擎同步以清理其资源
- 您要将
StartupUri="MainWindow.xaml"
留在应用程序中,请将其删除以简化使用固定代码的测试
执行测试的正确方法是启动一个 DispatcherTimer 并在那里拍摄第二张快照,对我来说,MainWindow 已经消失了。
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;
}