最近工作中,.Net 4.0环境下,WPF项目发现内存泄露问题,经过一系列跟踪分析,发现是Style中资源的动态资源引起的。具体什么原因,还未搞明白,特此写出来,跟码友交流。
先针对发现的问题,做了个小测试程序,如下。
MainWindow.xaml:
<Window x:Class="DynamicResourceLeakTest.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525">
<Grid Name="grid" />
</Window>
MainWindow.xaml.cs:
public MainWindow()
{
InitializeComponent();
// 加载资源
var resource = new ResourceDictionary();
resource.Source = new Uri("pack://application:,,,/DynamicResourceLeakTest;component/Resources/Styles.xaml");
this.Resources.MergedDictionaries.Add(resource);
// 添加控件
var control = new Control();
control.Style = this.TryFindResource("Custom_Style") as Style;
grid.Children.Add(control);
// 定时刷新控件
var timer = new DispatcherTimer();
timer.Interval = new TimeSpan(0, 0, 0, 0, 5);
timer.Tick += (s, ee) =>
{
control.IsEnabled = !control.IsEnabled;
};
timer.Start();
}
在Resources目录下存放的是两个资源文件
本文的重点在于Resources/Resource.xaml:
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<SolidColorBrush x:Key="CustomColor" Color="Gray" />
</ResourceDictionary>
Resources/Styles.xaml:<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:DynamicResourceLeakTest">
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="Resource.xaml" />
</ResourceDictionary.MergedDictionaries>
<Style x:Key="Custom_Style" TargetType="{x:Type Control}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Control}">
<Border Name="border" />
<ControlTemplate.Triggers>
<Trigger Property="IsEnabled" Value="False">
<Setter Property="Background" Value="{DynamicResource CustomColor}" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ResourceDictionary>
用.NET Memory Profiler 4.0 分析得到分析图如下,可见内存实例数在增加。在overview表中,看到实例数增加首当其冲的是System.WeakReference.
双击进去看调用堆栈情况:
正是我们在定时器里周期执行的设置control.IsEnabled操作。
在Style中的Trigger中,我么修改了Background的值,但是Background这值在模板中并未用到。相反,我将模板修改,用上Background,代码:
<Border Name="border" Background="{TemplateBinding Background}"/>
这样再运行监视下,没发现内存增加。
此现象具体原因不明,望指教。