我创建了一个简单的 wpf C#,其中包含一个文本框和 DispatcherTimer,它每秒显示一次调用 GC.GetTotalMemory(true) 的结果。返回的值随着每次调用而稳定增加,任务管理器显示私有工作内存集也增加。 这真的是内存泄漏吗,还是只是表面现象?在我的真实应用程序中,每个滴答内都会执行更多操作,内存泄漏显得明显更高。 我的代码如下
xaml
<Window x:Class="TestWPFApplication.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Window1" Height="300" Width="300">
<Grid>
<TextBox Name="memoryuseage"></TextBox>
</Grid>
</Window>
xaml.cs
namespace TestWPFApplication
{
/// <summary>
/// Interaction logic for Window1.xaml
/// </summary>
public partial class Window1 : Window
{
System.Windows.Threading.DispatcherTimer tmr;
public Window1()
{
InitializeComponent();
tmr = new System.Windows.Threading.DispatcherTimer();
tmr.Interval = new System.TimeSpan(0, 0, 1);
tmr.Tick += new EventHandler(StaticTick);
tmr.Start();
}
void StaticTick(object o, EventArgs sender)
{
memoryuseage.Text = GC.GetTotalMemory(true).ToString();
}
}
}
这真的是内存泄漏吗,还是只是表面现象?
只是外观。稳定的增加是正常的,除非你运行足够长的时间而导致它崩溃,否则它不是泄漏。但如果有小泄漏,这可能需要几天的时间。
您的内存使用量应该会趋于平稳,但只有在相当长的一段时间后才会如此。您可以通过在此处使用 GC.Collect() 来加快速度(每 10 个刻度左右)。
为了进行认真的诊断,您将需要内存分析器。
我认为您的代码中不会存在内存泄漏。内存增加只是因为GC.Collect()方法不是实时调用的。 试试这个:
void StaticTick(object o, EventArgs sender)
{
GC.Collect();
memoryuseage.Text = GC.GetTotalMemory(true).ToString();
}
这是由计时器调用的事件处理程序引起的内存泄漏。这是一个典型的事件处理程序泄漏。这里唯一的特殊情况是,只要存在对侦听器(注册的事件处理程序)的引用,计时器实例(事件发布者)就会保持活动状态。
WPF 计时器使用非托管的低级系统或操作系统计时器(这就是为什么有些实现IDisposable
,例如
System.Threading.PeriodicTimer
或
System.Threading.Timer
)。此非托管系统资源将保持活动状态,直到处置引用计时器对象或引用计时器对象显式释放计时器资源为止。
必须始终取消订阅计时器事件(甚至处置某些计时器的实例)。
例如,在Window
中,重写
Window.OnClosed
方法并取消订阅所有计时器:
public partial class Window1 : Window
{
System.Windows.Threading.DispatcherTimer timerThatUsesunmanagedSystemResources;
public Window1()
{
InitializeComponent();
this.timerThatUsesunmanagedSystemResources = new DispatcherTimer();
this.timerThatUsesunmanagedSystemResources.Interval = TimeSpan.FromSeconds(1);
this.timerThatUsesunmanagedSystemResources.Tick += OnTick;
this.timerThatUsesunmanagedSystemResources.Start();
}
private void OnTick(object sender, EventArgs e)
{
// TODO::Do something
}
protected override void OnClosed(EventArgs e)
{
base.OnClosed(e);
this.timerThatUsesunmanagedSystemResources.Stop();
// Unsubscribe ALL handlers from timer events
// to avoid the event handler leak
this.timerThatUsesunmanagedSystemResources.Tick -= OnTick;
}
}