在冻结后处置BitmapImage

时间:2017-01-22 22:39:56

标签: c# wpf image bitmapimage

我使用此功能创建了一个图像:

private BitmapImage LoadImage(byte[] imageData)
{
    if (imageData == null || imageData.Length == 0) return null;
    var image = new BitmapImage();
    using (var mem = new MemoryStream(imageData))
    {
        mem.Position = 0;
        image.BeginInit();
        image.CreateOptions = BitmapCreateOptions.PreservePixelFormat;
        image.CacheOption = BitmapCacheOption.OnLoad;
        image.UriSource = null;
        image.StreamSource = mem;
        image.EndInit();
    }
    image.Freeze();
    return image;
}

当我试图处理它时:

myImage.StreamSource.Close();
myImage.StreamSource.Dispose();

// Throws an exception since its frozen to read only
//myImage.StreamSource = null;

GC.Collect();

它不是由垃圾收集器收集的。可能因为我无法将其设置为null

如何处理此BitmapImage以便它在内存中的存活时间不长?

3 个答案:

答案 0 :(得分:0)

您已经处置了StreamSource,因为您在using语句中创建了MemoryStream,该语句在离开代码块时处理。 BitmapImage本身仅供管理,不需要处理。

你确定它没有被垃圾收集器清理干净吗?我有一个项目用BitmapImage制作了很多BitmapCacheOption.OnLoad个,我从来没有看到过它的内存泄漏。

(更新)在WPF中测试:(Update2)需要添加另一轮垃圾收集。出于某种原因,你必须调用它两次才能释放数组。

private async void Button_Click(object sender, RoutedEventArgs e)
{
    WeakReference test = this.TestThing();
    GC.Collect();
    GC.WaitForPendingFinalizers();
    GC.Collect();
    GC.WaitForPendingFinalizers();

    Debug.WriteLine(test.IsAlive); // Returns false
}

private WeakReference TestThing()
{
    byte[] imageData = File.ReadAllBytes(@"D:\docs\SpaceXLaunch_Shortt_3528.jpg");

    var image = new BitmapImage();
    using (var mem = new MemoryStream(imageData))
    {
        image.BeginInit();
        image.CreateOptions = BitmapCreateOptions.PreservePixelFormat;
        image.CacheOption = BitmapCacheOption.OnLoad;
        image.UriSource = null;
        image.StreamSource = mem;
        image.EndInit();
    }

    image.Freeze();

    return new WeakReference(image);
}

答案 1 :(得分:0)

我们可以使用以下代码调查此问题:

public static void Main()
{
    var readAllBytes = File.ReadAllBytes(@"SomeBitmap.bmp");
    var wr = new WeakReference(readAllBytes);
    var result = LoadImage(readAllBytes);
    readAllBytes = null;

    //result.StreamSource = null;

    result = null;
    GC.Collect();
    GC.WaitForPendingFinalizers();
    GC.Collect();
    GC.WaitForPendingFinalizers();
    Console.WriteLine($"IsAlive: {wr.IsAlive}");
    Console.ReadLine();
}

private static BitmapImage LoadImage(byte[] imageData)
{
    if (imageData == null || imageData.Length == 0) return null;
    var image = new BitmapImage();
    using (var mem = new MemoryStream(imageData))
    {
        mem.Position = 0;
        image.BeginInit();
        image.CreateOptions = BitmapCreateOptions.PreservePixelFormat;
        image.CacheOption = BitmapCacheOption.OnLoad;
        image.UriSource = null;
        image.StreamSource = mem;
        image.EndInit();
    }
    image.Freeze();
    return image;
}

我尝试了很多缓存设置,我找不到释放字节数组的方法,它看起来像是WPF中的一个错误。

如您所见,在2个GC集合之后,字节数组被释放。

编辑1:通过删除冻结简化BitmapImage并且Init方法确实释放字节数组:

private static BitmapImage LoadImage(byte[] imageData)
{
    if (imageData == null || imageData.Length == 0) return null;
    var image = new BitmapImage();
    using (var mem = new MemoryStream(imageData))
    {
        mem.Position = 0;
        image.CreateOptions = BitmapCreateOptions.PreservePixelFormat;
        image.UriSource = null;
        image.StreamSource = mem;
    }
    return image;
}

编辑2:我正如其他人所说,字节数组在2轮垃圾收集后被释放,我已经更新了顶级示例。我发现这非常有用,它表明框架不是黑盒子。

答案 2 :(得分:0)

就我而言,我需要在存储为服务器字节数组的一堆大型平面图之间进行切换。一旦将所有这些都加载到内存中,并且在它们之间进行了一些切换之后,我坐在3GB内存上,开始遇到所有加载的问题。图像本身的范围从600KB到几兆字节。绘制它们时,它们会占用更多空间。

我发现了一个包装流实现  此处(http://faithlife.codes/blog/2009/05/wrappingstream_implementation/),来自Bradley Grainger。

这使我的内存使用率相当低;大多数时候少于2GB。

WrapperStream MasterStream{get;set;}

private void ChangeImage()
{
            SelectedImage = null;
            GC.Collect();
            var stream = new MemoryStream(ImageSource);

            using (MasterStream = new WrappingStream(stream))
            {
                var bitmap = new BitmapImage();
                bitmap.BeginInit();
                bitmap.StreamSource = MasterStream;
                bitmap.CacheOption = BitmapCacheOption.OnLoad;
                bitmap.EndInit();
                bitmap.Freeze();
                SelectedImage = bitmap;
            }

            GC.Collect();

 }