2017-01-22 55 views
0

我创建了一个图像使用此功能后:一个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因此它不会在内存中活得更久?

回答

0

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

你确定它没有被垃圾收集器清理吗?我有一个项目,其中BitmapCacheOption.OnLoad有很多BitmapImage s,我从来没有见过内存泄漏。

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

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

我可以用此代码确认字节数组没有被释放,但仍有一个引用的地方。你可以通过给它添加一个'WeakReference'并检查'IsAlive'属性来测试它。我在发布模式下执行此操作时没有附加任何调试器。 – Stuart

+0

增加了一些测试代码。在这种情况下,我不确定为什么'IsAlive'对你来说是'真的'。如果我在同一个函数和Debug模式下调用了GC,它会返回'true',但是如果它处于相同的函数和释放模式下,它仍然是'false'。也许你可以分享你使用的确切代码? – RandomEngy

+0

@Stuart当跟踪传入内存流的字节数组时,我也会得到相同的结果。 – RandomEngy

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; 
} 

我已经试过的缓存设置的许多变化,我无法找到一个方法来释放字节数组,它看起来像一个bug在WPF中。

正如你所看到的,在2个GC集合之后,字节数组被释放。

编辑1:通过删除冻结简化BitmapImageInit方法做释放字节数组:

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轮垃圾收集发布,我已经更新了顶部的例子。我发现这非常有用,它表明该框架不是黑匣子。

+0

奇怪的是,如果我散装它。它也不会释放,除非我再做大量的图像。 – John

+0

我删除了冻结,现在当我创建一个新图像并调用EndInit()时,发生垃圾收集。 – John

+0

Thanks @John,我已验证您的发现并更新了我的答案,以包含它=) – Stuart