Border.Effect绑定泄漏内存但Border.Background没有

Pet*_*ter 7 c# wpf

使用更新的.NET 4.0,我发现了一个奇怪的内存泄漏,可以通过以下示例代码重现.

  • app.xml有一些应用程序范围的资源,它们绑定到app.xml.cs中的属性.创建泄漏的资源是<DropShadowEffect>Color依赖项属性绑定到App对象中的属性.
  • 主窗口有一个按钮,用于启动LeakWindow,其中使用app.xml中定义的资源.
  • 当泄漏窗口关闭时,它不会被垃圾收集.只有在泄漏窗口使用上述DropShadowEffect资源时才会发生这种情况.不会发生在一个SolidColorBrushColor也必然同一来源.

真的很感激,如果有人能告诉我为什么这个泄漏发生在DropShadowEffect但不是在SolidColorBrush

app.xml中

<Application x:Class="WpfSimple.App"
         xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
         xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
    <Application.Resources>
        <!--this one make GC unable to collect LeakWindow-->
        <DropShadowEffect x:Key="AppDropShadowColor" 
              Color="{Binding Source={x:Static Application.Current}, Path=DropShadowColor, Mode=OneWay}" />
        <!--this one does not leak-->
        <SolidColorBrush x:Key="AppBackground"
              Color="{Binding Source={x:Static Application.Current}, Path=DropShadowColor, Mode=OneWay}" />
    </Application.Resources>
</Application>
Run Code Online (Sandbox Code Playgroud)

App.xml.cs启动MainWindow并INotifyPropertyChanged为该属性实现DropShadowColor.

 public partial class App : Application, INotifyPropertyChanged
    {
        protected override void OnStartup(StartupEventArgs e)
        {
            base.OnStartup(e);        
            // start main window
            var mainWindow = new MainWindow();
            mainWindow.Show();
        }

        private Color _dropShadowColor = Colors.Blue;    
        public Color DropShadowColor
        {
            get { return _dropShadowColor; }    
            set {
                _dropShadowColor = value;
                OnPropertyChanged("DropShadowColor");
            }
        }

        public event PropertyChangedEventHandler PropertyChanged;
        protected virtual void OnPropertyChanged(string propertyName)
        {
            PropertyChangedEventHandler handler = PropertyChanged;
            if (handler != null) { handler(this, new PropertyChangedEventArgs(propertyName)); }
        }
    }
Run Code Online (Sandbox Code Playgroud)

MainWindow.xml和MainWindow.xml.cs有一个创建的按钮LeakWindow,如下所示.

var win = new LeakWindow {Owner = this};
win.Show();
Run Code Online (Sandbox Code Playgroud)

还有另一个按钮要做GC.Collect();

LeakWindow.xml

<Window x:Class="WpfSimple.LeakWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="Leak" Height="300" Width="300">
  <Grid>
    <!--leak-->
    <Border Width="200" Height="200" BorderThickness="1" BorderBrush="Black" Effect="{StaticResource AppDropShadowColor}"/>

    <!--no leak if comment out above and uncomment below-->
    <!--<Border  Width="200" Height="200" BorderThickness="1" BorderBrush="Black" Background="{StaticResource AppBackground}"/>-->
  </Grid>
</Window>
Run Code Online (Sandbox Code Playgroud)

LeakWindow.xml.cs

 public partial class LeakWindow : Window
    {
        public LeakWindow()
        {
            InitializeComponent();
        }    

        ~LeakWindow()
        {
            Debug.WriteLine("LeakWindow finalized");
        }
    }
Run Code Online (Sandbox Code Playgroud)

更新

  • 我的搜索显示这可能与DynamicResource\StaticResource相关, 导致内存泄漏.但是那个在.NET 3.5(kb967328)中早期修补了.还尝试了该线程中提到的WalkDictionary方法但没有帮助.
  • 更改绑定模式OneTime也无济于事.
  • 切换到.NET 4.5(也修补到目前为止)并使用DynamicResource没有帮助.

进一步的调查表明的泄漏是由一个导致EventHandler从参考DropShadowEffectBorder.Effect.可能是由于绑定而导致的更改通知DropShadowEffect.
仍然,奇怪的是为什么这只发生在Border.Effect但不发生Border.Background

Workground
添加x:Shared=false<DropShadowEffect>在app.xml中可以解决此.我现在可以拥有应用程序范围内定义的资源,但会失去内存效率.

Gle*_*mas 2

我认为问题是由DropShadowEffect附加到视觉树的方式引起的。将其DropShadowEffect移入您的控件模板而不是将其作为资源可能会解决泄漏问题,但随后您将失去该共享资源......