Die Verwendung von MemoryStream führt zu einer nicht ausreichenden Speicherausnahme

7

Ich habe Probleme, wenn ich MemoryStream mehrfach benutze.

Beispiel:

%Vor%

Dieser Codeabschnitt durchläuft Bilder auf einer Seite einer PDF-Datei. Die Datei kann bis zu 500 Seiten haben, sagen wir 5 Bilder auf jeder Seite. Es führt zu Tausenden von Iterationen. Das Problem besteht darin, dass der MemoryStream nicht freigegeben wird und zu Ausnahmen wegen nicht verfügbarem Arbeitsspeicher führt. Das X-Image hat normalerweise ungefähr 250 kB.

Ich benutze hier die Aspose.PDF-Bibliothek, um mit PDF zu arbeiten (XImage ist eine Klasse aus dieser Bibliothek), aber das spielt keine Rolle. Ich habe versucht, ein einfaches Beispiel zu erstellen, bei dem ich einfach einen neuen MemoryStream erstelle und eine Dummy-Bitmap darin speichere. Es führt zu den gleichen Problemen.

Ich habe auch versucht, FileStream anstatt MemoryStream zu verwenden, aber es verhält sich genauso.

Jede Hilfe wird geschätzt.

Danke

Jiri

    
Jiri Matejka 25.06.2013, 20:04
quelle

1 Antwort

20

Der Speicher aus dem Stream wird freigegeben. Ich verspreche es dir. Wirklich, ist es.

Was nicht freigegeben wird, ist der Adressraum in Ihrer Anwendung, der früher von diesem Speicher belegt war. Es gibt eine Menge von RAM für Ihren Computer verfügbar, aber Ihre spezielle Anwendung stürzt ab, weil sie keinen Platz in der Adresstabelle finden kann, um weitere zuzuweisen.

Der Grund dafür, dass Sie das Limit erreicht haben, besteht darin, dass der MemoryStream den Puffer bei seinem Wachstum recycelt. Er verwendet intern ein Byte [], um seine Daten zu speichern, und das Array wird standardmäßig auf eine bestimmte Größe initialisiert. Wenn Sie beim Schreiben in den Stream die Größe Ihres Arrays überschreiten, verwendet der Stream einen Doubling-Algorithmus, um neue Arrays zuzuordnen. Informationen werden dann vom alten Array in das neue Array kopiert. Danach kann und wird das alte Array gesammelt, aber es wird nicht kompaktiert (think: defragged). Das Ergebnis sind Lücken in Ihrem Adressraum, die nicht mehr verwendet werden. Ein MemoryStream verwendet möglicherweise mehrere Arrays, was zu mehreren Speicherlöchern führt, die einen Gesamtadressraum darstellen, der möglicherweise viel größer ist als die Quelldaten.

AFAIK, zu diesem Zeitpunkt gibt es keine Möglichkeit, den Speicherbereiniger zu zwingen, den Speicheradressraum zu komprimieren. Die Lösung besteht also darin, einen großen Block zuzuordnen, der mit Ihrem größten Bild umgehen kann, und diesen Block dann immer wieder zu verwenden, so dass Sie nicht mit Speicheradressen enden, die nicht erreicht werden können.

Für diesen Code bedeutet dies, dass der Speicherstream außerhalb der Schleife erstellt wird und eine Ganzzahl an den Konstruktor übergeben wird, damit sie auf eine angemessene Anzahl von Bytes initialisiert wird. Sie werden feststellen, dass dies auch einen schönen Leistungsschub bringt, da Ihre Anwendung plötzlich nicht mehr häufig Daten von einem Byte-Array in ein anderes kopiert, was bedeutet, dass dies die bessere Option ist, selbst wenn Sie Ihre Adressentabelle komprimieren könnten:

%Vor%     
Joel Coehoorn 25.06.2013 20:16
quelle

Tags und Links