Bildladespeicherverlust mit C #
-
19-09-2019 - |
Frage
Ich habe einen Speicherverlust Problem in meiner Anwendung, die Lasten eine große Menge von Bildern. Ich bin ziemlich neu in C # und dachte, meine Tage von Speicherverlust Fragen waren vorbei. Ich kann nicht herauszufinden, das Problem - vielleicht verwende ich einige nicht verwaltete Module, die ich nicht richtig behandeln werde?
Zu meinem Problem illustrieren Ich habe den Kern vereinfacht, was das Problem verursacht und bewegt diese in ein sauberes Projekt. Beachten Sie, dass das alles albern Code ist, die nicht die ursprüngliche Anwendung reflektiert es kam. In der Testanwendung habe ich 2 Tasten, die Auslösung zwei Ereignisse.
Button 1 - erstellen: ein Objekt in die Datacontext einstellen. Dadurch werden die Bilder laden und sie am Leben zu halten, indem das Objekt an die Datacontext Einstellung:
var imgPath = @"C:\some_fixed_path\img.jpg";
DataContext = new SillyImageLoader(imgPath);
Button 2 - CleanUp: Mein Verständnis ist, dass, wenn ich von der Referenz gehen lassen die SillyImageLoader hält, die wiederum die Bilder hält, dann wird diese gelöscht werden. I triggern auch explizit garbage collection nur sofort die Größe des Speichers, um zu sehen, nachdem die Referenz fallen.
DataContext = null;
System.GC.Collect();
Bei der Prüfung, ich bin ein 974KB JPEG-Bild zu laden. Halten 30 Bitmap-Darstellungen dieser die Speicherauslastung von meiner Anwendung von ~ 18MB bis 562MB ~ steigert. Okay. Aber wenn ich Bereinigung traf der Speicher fällt nur auf ~ 292MB. Wenn ich erstellen + CleanUp wiederholen Ich bin mit einem anderen ~ 250 MB Speicherplatz zur Verfügung. So offensichtlich ist noch etwas von jemandem gehalten.
Hier ist der 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());
}
}
}
}
Irgendwelche Ideen? Das Problem scheint mit dem Bitmap zu sein. nur die Bitmap Holding gibt es kein Speicherleck. Ich bin mit Bitmap Lage sein, dies von einem Bild auf die Source-Eigenschaft einzustellen. Soll ich das anders machen? Wenn ja - ich würde immer noch wie die Antwort den Speicherverlust wissen.
Danke.
Lösung
Wenn Sie anrufen
bmp.GetHbitmap()
eine Kopie des Bitmap erzeugt. Sie werden einen Verweis auf den Zeiger auf dieses Objekt und Anruf
halten müssenDeleteObject(...)
auf sie.
hier :
Bemerkungen
Sie sind verantwortlich für den anruf GDI DeleteObject Methode der freien Speicher durch das GDI-Bitmap-Objekt verwendet wird.
Unter Umständen können Sie sich die Kopfschmerzen sparen (und Overhead) des Kopierens der Bitmap unter Verwendung von Bitmap statt Bitmapsource. Auf diese Weise können Sie in einem Schritt laden und erstellen.
Andere Tipps
Sie müssen die GDI DeleteObject
Methode auf dem IntPtr
Zeiger von GetHBitmap () zurückgegeben nennen. Die IntPtr
aus dem Verfahren zurückgeführt ist, ein Zeiger auf die Kopie des Objekts in dem Speicher. Dies muss manuell mit dem folgenden Code befreit werden:
[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;
}
Es scheint, dass, wenn Sie GetHBitmap () aufrufen, sind Sie verantwortlich für die Freigabe des Objekts
[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);
}
Ich vermute, dass die Bitmapsource übernehmen die Verantwortung nicht für dieses Objekt zu befreien.