2009-11-11 17 views
10

Mam problem wycieków pamięci w mojej aplikacji, który ładuje dużą liczbę obrazów. Jestem raczej nowy w C# i pomyślałem, że minęły moje dni z problemami z wyciekiem pamięci. Nie mogę wymyślić problemu - może używam niektórych niezarządzanych modułów, których nie obsługuję poprawnie?Obrazek wyciek pamięci z C#

Aby zilustrować mój problem, uprościliśmy rdzeń tego, co powoduje problem, i przeniosłem go do czystego projektu. Zauważ, że jest to cały głupi kod, który nie odzwierciedla oryginalnej aplikacji, z której pochodzi. W aplikacji testowej mam 2 przyciski, wyzwalające dwa zdarzenia.

Przycisk 1 - Utwórz: Ustawienie obiektu na format danych. Spowoduje to załadowanie obrazów i zachować je przy życiu przez ustawienie obiekt do DataContext:

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

przycisk 2 - Oczyszczanie: Rozumiem, że jeśli puścić odniesienia trzymając SillyImageLoader który ponownie przechowuje obrazy, a następnie to zostanie usunięte. Wywoływam również jawnie wyrzucanie śmieci tylko po to, aby natychmiast zobaczyć ilość pamięci po usunięciu odwołania.

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

Podczas testowania ładuję obraz jpeg 974KB. Przechowywanie 30 obrazów bitmapowych zwiększa wykorzystanie pamięci mojej aplikacji z ~ 18 MB do ~ 562 MB. Ok. Ale po wyczyszczeniu pamięci ilość pamięci spada do ~ 292 MB. Jeśli powtórzę Create + CleanUp, pozostawi mi kolejną pamięć ~ 250 MB. Więc oczywiście coś jest nadal w posiadaniu kogoś.

Oto SillyImageLoader-code:

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

Jakieś pomysły? Problem wydaje się być związany z BitmapSource. Trzymając tylko bitmapę, nie ma wycieku pamięci. Używam BitmapSource, aby móc ustawić to na właściwość Source obrazu. Czy powinienem zrobić to inaczej? Jeśli tak, nadal chciałbym znać odpowiedź na wyciek pamięci.

Dzięki.

+0

Kto dzwoni Pozbyć się BitmapSource zwrócony z LoadImage? –

+0

Pomyślałem o tym, rzeczywiście opublikowałem odpowiedź na podstawie tego, ale nie widzę dyspozycji na BitmapSource (usunąłem odpowiedź) –

+0

W jaki sposób monitorujesz wykorzystanie pamięci przez twoją aplikację? Menadżer zadań? –

Odpowiedz

13

Po wywołaniu

bmp.GetHbitmap() 

tworzona jest kopia bitmapy. Będziesz musiał zachować odniesienie do wskaźnika do tego obiektu i zadzwonić pod ten numer i zadzwonić pod ten numer.

Od here:

Uwagi

jesteś odpowiedzialny za wywołanie metodę GDI DeleteObject uwolnić pamięć używany przez obiekt bitmapy GDI.


Możesz być w stanie oszczędzić sobie bólu głowy (i narzut) kopiowania bitmapę za pomocą BitmapImage zamiast BitmapSource. Umożliwia to załadowanie i utworzenie w jednym kroku.

+0

Jest to absolutnie poprawne i sprawiło, że problemy z pamięcią zniknęły! Dzięki! I do innych z podobnymi odpowiedziami! Masz rację, że mogę użyć BitmapImage w tym konkretnym przykładzie. Jednak w mojej aktualnej aplikacji nie mam ścieżki do pliku obrazu, ale pobieram Bitmapy z innego obiektu biblioteki trzeciej. Naprawdę niewiele mogę z tym zrobić. Więc nie mogę użyć Uri - muszę użyć bitmapy ... – stiank81

7

Należy wywołać metodę GDI DeleteObject na wskaźniku zwrócony z GetHBitmap(). Zwrócone z tej metody jest wskaźnik do kopii obiektu w pamięci.To musi być uwolniony ręcznie za pomocą następującego kodu:

[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

Wydaje się, że podczas rozmowy GetHBitmap() jesteś odpowiedzialny za uwolnienie obiekt

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

Zgaduję, że nie BitmapSource wziąć odpowiedzialność za uwolnienie tego obiektu.