BitmapSource绑定内存泄漏

时间:2015-08-03 14:36:30

标签: wpf

问题与BitmapImage相同,但为了演示,我们使用BitmapSource。基本上,如果我在视图中显示一个相当大的图像,并且在我完成它时将绑定对象设置为null,则无论您等待多长时间,图像内存都不会被回收。重现问题的完整代码:

XAML:

<Window x:Class="TestBitmapSource.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:local="clr-namespace:TestBitmapSource"
    Title="MainWindow" Height="350" Width="525">
<Window.DataContext>
    <local:MainViewModel/>
</Window.DataContext>
<Grid>
    <StackPanel>
        <Button Command="{Binding Path=ReloadCommand}">Load</Button>
        <Button Command="{Binding Path=ReleaseCommand}">Release</Button>
        <Image Source="{Binding Path=MyImage}"></Image>
    </StackPanel>
</Grid>

C#:

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;

namespace TestBitmapSource
{
class MainViewModel : INotifyPropertyChanged
{
    private BitmapSource myImage;
    public BitmapSource MyImage
    {
        get { return myImage; }
        set { myImage = value; NotifyPropertyChanged(); }
    }

    public event PropertyChangedEventHandler PropertyChanged;
    private void NotifyPropertyChanged([CallerMemberName] String propertyName = "") 
    { 
        if (PropertyChanged != null)
        {
            PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
        }
    }

    private ICommand _reloadCommand;
    public ICommand ReloadCommand 
    { 
        get 
        { 
            return _reloadCommand ?? (_reloadCommand = new CommandHandler(() => Reload(), true)); 
        } 
    }
    private ICommand _releaseCommand;
    public ICommand ReleaseCommand
    {
        get
        {
            return _releaseCommand ?? (_releaseCommand = new CommandHandler(() => Release(), true));
        }
    }


    Random random = new Random();
    public void Reload()
    {
        int width = 6000;
        int height = 6000;
        int b = random.Next();
        int g = random.Next();
        int r = random.Next();
        unsafe
        {
            byte* pixels = (byte*)Marshal.AllocHGlobal(width * height * 3);
            for (int i = 0; i < width * height; i++)
            {
                pixels[i * 3] = (byte)b;
                pixels[i * 3 + 1] = (byte)g;
                pixels[i * 3 + 2] = (byte)r;
            }
            MyImage = BitmapSource.Create(width, height, 96, 96, PixelFormats.Bgr24, null, (IntPtr)pixels, width * height * 3, width * 3);
            Marshal.FreeHGlobal((IntPtr)pixels);
            MyImage.Freeze();
        }
    }

    public void Release()
    {
        MyImage = null;
        GC.Collect();
        GC.WaitForPendingFinalizers();
    }

    public MainViewModel()
    {
        MyImage = null;
    }
}

public class CommandHandler : ICommand 
{ 
    private Action _action; private bool _canExecute; 
    public CommandHandler(Action action, bool canExecute) { _action = action; _canExecute = canExecute; } 
    public bool CanExecute(object parameter) { return _canExecute; } 
    public event EventHandler CanExecuteChanged; 
    public void Execute(object parameter) { _action(); } 
}
}

要重现此问题,请单击“加载”按钮(将使用BitmapSource.Create创建图像),然后单击“释放”按钮,该按钮将尝试释放图像并收集未使用的内存。但是,进程监视器显示250MB的内存仍在使用中,如果我在Visual Studio中使用对象ID标记它,我知道MyImage对象仍然存活。

到目前为止,我发现了两件事:

1)如果我没有绑定到视图中的图像,一切都很好,图像对象立即消失。

2)如果再次单击“释放”按钮,标记的对象会突然消失并回收内存。

很明显,绑定行为会在视图模型中将图像对象设置为null时在某处创建一些未释放的引用。我想在进入内存分析器之前检查是否遗漏了一些明显的东西。

由于

0 个答案:

没有答案