Aufgrund scheinbar voreiliger Out of Memory-Ausnahmen haben wir die Speicherbelegung verschiedener .NET-Konstrukte genau untersucht ... besonders große Objekte, die dazu neigen Fragmentieren Sie den Large Object Heap, was zu vorzeitigen Out of Memory-Ausnahmen führt. Ein Bereich, der ein wenig überraschend ist, sind die .NET Image-Klassen: Bitmap und Metafile.Die Speicherauslastung und Fragmentierung von .NET-Bildklassen: Bitmap vs. Metafile
Hier ist, was wir denken, wir haben gelernt, aber nicht gelungen, MS-Dokumentation zu finden, um zu überprüfen, so würden wir keine Bestätigung andere schätzen geben kann:
(1) Wenn Sie ein Bitmap-Objekt aus einem komprimierten Raster erstellen Datei (JPG, PNG, GIF, etc), verbraucht es Speicher für ein vollständig unkomprimiertes Pixel-Array mit der vollen Auflösung dieser Datei. So würde zum Beispiel ein 5MB JPG, das 9000x3000 Pixel groß ist, in 9000x3000x3 Bytes (angenommen 24 Bit Farbe, kein Alpha) oder 81MB Speicherbedarf erweitert. Richtig?
(1a) Es gibt einige Beweise (siehe 2b unten), dass es auch das ursprüngliche komprimierte Format speichert ... also eigentlich 86MB in diesem Fall. Aber das ist unklar ... weiß jemand?
(2) Wenn Sie ein Metafile-Objekt erstellen und dann eine Raster-Datei (JPG, PNG, GIF usw.) darauf zeichnen, wird nur Speicher für die komprimierte Datei benötigt. Wenn Sie also ein 5MB JPG mit 9000x3000 Pixeln in eine Metafile zeichnen, verbraucht es nur ungefähr 5MB Speicher. Richtig?
(2a) Um eine Rasterdatei in ein Metafile-Objekt zu zeichnen, scheint der einzige Weg darin zu bestehen, ein Bitmap mit der Datei zu laden und dann die Bitmap in die Metafile zu zeichnen. Gibt es einen besseren Weg, der nicht das zeitweilige Laden dieser riesigen Bitmap-Daten beinhaltet (und die damit verbundene Speicherfragmentierung verursacht)?
(2b) Wenn Sie eine Bitmap in eine Metafile zeichnen, wird ein komprimiertes Format verwendet, dessen Größe der ursprünglichen komprimierten Datei ähnelt. Wird die ursprüngliche komprimierte Datei in der Bitmap gespeichert? Oder macht es das, indem es die erweiterte Bitmap mit den ursprünglichen Komprimierungseinstellungen komprimiert?
(3) Wir gingen ursprünglich davon aus, dass große (> 85 KB) Image-Objekte im Large Object Heap platziert werden. In der Tat scheint das nicht der Fall zu sein. Vielmehr ist jedes Bitmap und jede Metafile ein 24-Byte-Objekt im Small Object Heap, das sich auf einen Block Native Memory bezieht, der die echten Daten enthält. Richtig?
(3a) Wir gehen davon aus, dass Native Memory wie Large Object Heap ist, da sie nicht kompaktiert werden kann ... sobald das große Objekt in Native Memory gelegt wird, wird es niemals verschoben und kann daher die Fragmentierung des Native Memory verursachen so viele Probleme wie die Fragmentierung von Large Object Heap. Wahr? Oder gibt es eine spezielle Behandlung der zugrunde liegenden Bitmap/Metafile-Daten, die effizienter ist?
(3b) Also, es scheint vier unabhängige Speicherblöcke zu geben, die separat verwaltet werden, und jedes davon kann zu den gleichen Out-of-Memory-Ausnahmen führen: Small Object Heap (gemanagte Objekte < 85KB, kompaktiert durch GC), Large Object Heap (verwaltete Objekte> 85 KB, die von GC gesammelt, aber nicht komprimiert werden), Native Memory (nicht verwaltete Objekte, vermutlich nicht komprimiert) und Desktop Heap (wobei Windows-Handles und solche begrenzten Ressourcen verwaltet werden). Habe ich diese vier richtig dokumentiert? Gibt es andere, denen wir uns bewusst sein sollten?
Jede Klarheit, die jemand auf dem oben genannten bereitstellen kann, würde sehr geschätzt werden. Wenn es ein gutes Buch oder einen Artikel gibt, der das oben genannte vollständig erklärt, lass es mich wissen. (Ich bin glücklich, die erforderliche Lektüre zu machen; aber die überwiegende Mehrheit der Bücher nicht so tief, und damit sagen Sie mir nichts, was ich nicht bereits weiß.)
Vielen Dank!
GDI + speichert Bilddaten im nicht verwalteten Speicher. Also keine Ihrer Annahmen sind nah. Das Vergessen von Bitmaps ist eine schnelle Möglichkeit, OOM zu treffen. Die Adressraumfragmentierung ist eine andere, ein 32-Bit-Prozess erreicht nach einer Weile 90 MB als Gefahrenzone. –
"keine Ihrer Annahmen sind nahe" ?? Aber alles, was du darüber sagst, stimmt mit meinen obigen Annahmen überein ... also, nicht sicher, was du mir erzählst. Wollen Sie sagen, dass "Native Memory" anders ist als "Unmanaged Memory"? Oder meintest du "alle deine Annahmen sind nah"? –
Auf 2 (a), das Beste, was ich bisher gefunden habe, ist "Nein, aber ja, sorta ...": Erstellen Sie eine separate .exe, die einfach eine beliebige Rasterdatei in eine Bitmap einliest und dann eine neue Metafile öffnet Datei auf der Festplatte und führt DrawImage dieser Bitmap in diese Metafile und beendet.Wenn ich dann in meiner Hauptanwendung auf eine Rasterdatei zeige, anstatt sie zu öffnen und eine Speicherfragmentierung in meiner Hauptanwendung zu erzeugen, rufe ich diese .exe auf, um eine temporäre Datei zu erstellen, die eine Metafile enthält, und dann in meiner Hauptanwendung lese diese Datei direkt in eine Metafile ein. Clunky, aber keine Auswirkungen auf die Hauptanwendung. Irgendwelche besseren Ideen? –