2017-01-22 17 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