2009-11-11 59 views
10

我在我的應用程序中加載大量圖像時出現內存泄漏問題。我對C#很陌生,並認爲我的內存泄漏問題已經過去了。我無法弄清楚這個問題 - 也許我正在使用一些我無法正確處理的非託管模塊?使用C加載內存泄漏圖像#

爲了說明我的問題,我簡化了導致問題的原因的核心,並將其轉移到一個乾淨的項目中。請注意,這是所有愚蠢的代碼,它並不反映它來自的原始應用程序。在測試應用程序中,我有2個按鈕,觸發兩個事件。

按鈕1 - 創建:將對象設置爲datacontext。這將加載圖像,並讓他們活着通過設置對象到DataContext:

var imgPath = @"C:\some_fixed_path\img.jpg"; 
DataContext = new SillyImageLoader(imgPath); 

按鈕2 - 清理:我的理解是,如果我放手的參考拿着這又保持圖像的SillyImageLoader,然後這將被刪除。我也顯式地觸發垃圾收集,以便在刪除引用後立即查看內存量。

DataContext = null; 
System.GC.Collect(); 

測試時我加載了974KB jpeg圖像。保存30位圖表示可以將我的應用程序的內存使用量從〜18MB提高到〜562MB。好。但是當我清理時,內存只能降到292MB。如果我重複創建+清理,我剩下另一個〜250MB內存。所以顯然有些東西仍然由某人持有。

這裏是SillyImageLoader代碼:

namespace MemoryLeakTest 
{ 
    using System; 
    using System.Drawing; 
    using System.Windows; 
    using System.Windows.Interop; 
    using System.Windows.Media.Imaging; 

    public class SillyImageLoader 
    { 
     private BitmapSource[] _images; 

     public SillyImageLoader(string path) 
     { 
      DummyLoad(path); 
     } 

     private void DummyLoad(string path) 
     { 
      const int numberOfCopies = 30; 
      _images = new BitmapSource[numberOfCopies]; 

      for (int i = 0; i < numberOfCopies; i++) 
      { 
       _images[i] = LoadImage(path); 
      } 
     } 

     private static BitmapSource LoadImage(string path) 
     { 
      using (var bmp = new Bitmap(path)) 
      { 
       return Imaging.CreateBitmapSourceFromHBitmap(
        bmp.GetHbitmap(), 
        IntPtr.Zero, 
        Int32Rect.Empty, 
        BitmapSizeOptions.FromEmptyOptions()); 
      }    
     } 
    } 
} 

任何想法?該問題似乎與BitmapSource。只保存位圖沒有內存泄漏。我使用BitmapSource可以將其設置爲圖像的Source屬性。我應該這樣做嗎?如果是這樣 - 我仍然想知道內存泄漏的答案。

謝謝。

+0

誰在調用從LoadImage返回的BitmapSource上的Dispose? – 2009-11-11 12:29:26

+0

我以爲這,確實發佈了一個基於它的答案,但我看不到在BitmapSource上的配置(我已經刪除了答案) – 2009-11-11 12:30:28

+0

你如何監控你的應用程序的內存使用情況?任務管理器? – 2009-11-11 12:36:13

回答

13

當你調用

bmp.GetHbitmap() 

創建位圖的副本。您需要保留對該對象指針的引用,並致電

DeleteObject(...) 

就可以了。

here

備註

你是負責調用 GDI DeleteObject的方法來釋放由GDI位圖對象所使用的內存 。


您可以通過使用BitmapImage代替的BitmapSource保存自己複製位圖的頭痛(和開銷)。這使您可以在一個步驟中加載和創建。

+0

這是絕對正確的,並使內存問題消失!謝謝!而thx對其他人有相似的答案!你說得對,我可以在這個特定的例子中使用BitmapImage。但是,在我的實際應用程序中,我沒有指向圖像文件的路徑,而是從第三方庫的另一個對象獲取Bitmaps。對此我沒有太多可做的事情。所以我不能使用Uri - 我需要使用位圖。 – stiank81 2009-11-11 14:02:18

7

您需要在GetHBitmap()返回的IntPtr指針上調用GDI DeleteObject方法。從方法返回的IntPtr是一個指向內存中對象副本的指針。這必須使用下面的代碼進行手動釋放:

[System.Runtime.InteropServices.DllImport("gdi32.dll")] 
public static extern bool DeleteObject(IntPtr hObject); 

private static BitmapSource LoadImage(string path) 
{ 

    BitmapSource source; 
    using (var bmp = new Bitmap(path)) 
    { 

     IntPtr hbmp = bmp.GetHbitmap(); 
     source = Imaging.CreateBitmapSourceFromHBitmap(
      hbmp, 
      IntPtr.Zero, 
      Int32Rect.Empty, 
      BitmapSizeOptions.FromEmptyOptions()); 

     DeleteObject(hbmp); 

    } 

    return source; 
} 
5

看來,當你調用GetHBitmap()你是負責釋放對象

[System.Runtime.InteropServices.DllImport("gdi32.dll")] 
public static extern bool DeleteObject(IntPtr hObject); 

private void DoGetHbitmap() 
{ 
    Bitmap bm = new Bitmap("Image.jpg"); 
    IntPtr hBitmap = bm.GetHbitmap(); 

    DeleteObject(hBitmap); 
} 

我猜的的BitmapSource不負責釋放這個對象。