2009-11-11 19 views
10

アプリケーションで大量の画像を読み込むメモリリークの問題があります。私はむしろC#の初心者ですし、メモリリークの問題の私の日が過去だったと思いました。私は問題を理解することができません - おそらく私は正しく処理されていないいくつかの管理されていないモジュールを使用していますか?C#でメモリリークが発生する画像

私の問題を説明するために、私は問題の原因を明確にし、これをクリーンなプロジェクトに移しました。これは元々のアプリケーションが反映されていないばかげたコードであることに注意してください。テストアプリケーションでは、2つのイベントを発生させる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に向上します。 OK。しかし、私がクリーンアップするとメモリは〜292MBにしか落ちません。私がCreate + CleanUpを繰り返すと、別の〜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を使ってこれをImageのSourceプロパティに設定することができます。私はこれを違うはずか?もしそうなら、私はまだメモリリークの答えを知りたいです。

ありがとうございました。

+0

誰が呼び出しているのですか?LoadImageから返されたBitmapSourceを破棄しますか? –

+0

私はこれを実際にそれに基づいて答えを投稿したと思ったが、BitmapSource(私は答えを削除しました)で処分を見ることができません –

+0

あなたはどのようにアプリケーションのメモリ使用量を監視していますか?タスクマネージャー? –

答えて

13

ビットマップのコピーが作成され

bmp.GetHbitmap() 

を呼び出します。あなたはそのオブジェクトへのポインタへの参照を保持し、その上に

DeleteObject(...) 

を呼び出す必要があります。 hereから

あなたはGDIビットマップオブジェクトが使用 メモリを解放するために GDI DeleteObjectのメソッドを呼び出すための責任がある備考


あなたは代わりにしたBitmapSourceのBitmapImageを使って自分でビットマップをコピーするの頭痛(とオーバーヘッド)を保存することができるかもしれません。これにより、1つのステップでロードして作成することができます。

+0

これは絶対に正しいことであり、メモリの問題は解消されました!どうも!同様の答えを持つ他の人たちにもうってつけです!この特定の例でBitmapImageを使うことができます。しかし、私の実際のアプリケーションでは、イメージファイルへのパスはありませんが、サードパーティのライブラリの別のオブジェクトからビットマップを取得しています。これについて私ができることはあまりありません。だから私はUriを使うことができない - ビットマップを使う必要がある。 – stiank81

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がないことを推測しているオブジェクトを解放する責任があると思われますこのオブジェクトを解放する責任を負います。

関連する問題