Java: Speicher effizient ByteArrayOutputStream
Habe ich eine 40MB Datei in der Festplatte und ich muss auf "Karte", die es in den Speicher mit einem byte-array.
Zuerst dachte ich, dass das schreiben der Datei in ein ByteArrayOutputStream wäre der beste Weg, aber ich finde es dauert etwa 160 MB heap-Speicher in einem bestimmten Augenblick während des Kopiervorgangs.
Weiß jemand einen besseren Weg, dies zu tun, ohne Verwendung von drei mal die Größe der Datei RAM?
Update: Dank für Eure Antworten. Ich bemerkte, dass ich konnte der Speicherverbrauch ein wenig erzählen ByteArrayOutputStream ersten Größe, ein bisschen größer als der original-Dateigröße (über die genaue Größe mit meinem code Kräfte Umverteilung, haben zu prüfen, warum).
Gibt es ein weiteres high-memory-spot: wenn ich byte[] zurück mit ByteArrayOutputStream.toByteArray. Ein Blick auf den source code ich es sehen kann, ist das Klonen array:
public synchronized byte toByteArray()[] {
return Arrays.copyOf(buf, count);
}
Dachte ich, ich könnte einfach erweitern ByteArrayOutputStream und schreiben Sie diese Methode, also zur Rückgabe des original-array direkt. Gibt es eine potenzielle Gefahr ist hier, da der stream und das byte-array nicht mehr als einmal verwendet werden?
InformationsquelleAutor der Frage user683887 | 2011-08-31
Du musst angemeldet sein, um einen Kommentar abzugeben.
MappedByteBuffer
könnte das sein, was du bist suchen für.Ich bin überrascht, dass es so viel RAM zum Lesen einer Datei im Speicher, obwohl. Haben Sie konstruiert die
ByteArrayOutputStream
mit einer entsprechenden Kapazität? Wenn Sie noch nicht, der stream konnte zuteilen ein neues byte-array, wenn es in der Nähe des Ende der 40 MB, was bedeutet, dass Sie, zum Beispiel, haben einen vollen Puffer von 39 MB, und einen neuen Puffer zweimal die Größe. In der Erwägung, dass, wenn der stream hat die entsprechende Kapazität, wird es keine Umverteilung (schneller), und kein verschwendeter Speicher.InformationsquelleAutor der Antwort JB Nizet
ByteArrayOutputStream
sollte okay sein, so lange wie Sie angeben, eine geeignete Größe im Konstruktor. Es wird noch eine Kopie erstellen, wenn Sie anrufentoByteArray
aber das ist nur temporäre. Tun Sie wirklich Sinn den Speicher kurz gehen, bis eine Menge?Alternativ, wenn Sie bereits wissen, die Größe, mit zu beginnen, können Sie einfach erstellen Sie ein byte-array und wiederholt gelesen von einem
FileInputStream
in die Puffer, bis Sie haben alle Daten.InformationsquelleAutor der Antwort Jon Skeet
Wenn Sie wirklich wollen, um Karte die Datei in den Speicher, dann ein
FileChannel
ist der entsprechende Mechanismus.Wenn alles, was Sie tun möchten, ist die Datei Lesen, in eine einfache
byte[]
(und müssen keine änderungen an diesem array werden sich wieder auf der Datei), dann einfach das Lesen in einer angemessen bemessenenbyte[]
aus einer normalenFileInputStream
sollte ausreichen.Guave hat
- Dateien.toByteArray()
die tut alles für Sie.InformationsquelleAutor der Antwort Joachim Sauer
Zur Erläuterung der Puffer das Wachstum Verhalten
ByteArrayOutputStream
Lesen Sie bitte diese Antwort.Antwort auf deine Frage, es ist sicher zu erweitern
ByteArrayOutputStream
. In deiner situation ist es wahrscheinlich besser, das überschreiben der write-Methoden so, dass die maximale zusätzliche Zuteilung begrenzt ist, sprich, bis zu 16MB. Sollten Sie nicht außer KrafttoByteArray
um Zugang zu den geschützten buf[] Mitglied. Dies ist, weil ein stream ist nicht a-Puffer; Einen stream ein Ringpuffer, der hat eine position, die Zeiger und die Grenze Schutz. So, es ist gefährlich, den Zugang und potenziell manipulieren Puffer von außerhalb der Klasse.InformationsquelleAutor der Antwort Derek Bennett
Wenn du 40 MB Daten sehe ich keinen Grund, warum es dauern würde, mehr als 40 MB zu erstellen, die ein byte[]. Ich nehme an, Sie sind mit einer wachsenden ByteArrayOutputStream erzeugt einen byte[] zu kopieren, wenn Sie fertig sind.
Können Sie versuchen, die alten Lesen Sie die Datei auf einmal Ansatz.
Mit einem MappedByteBuffer ist effizienter und vermeidet eine Kopie der Daten (oder mit Hilfe der heap viel), vorausgesetzt, Sie können die ByteBuffer direkt, aber wenn Sie verwenden ein byte[] seine unwahrscheinlich viel helfen.
InformationsquelleAutor der Antwort Peter Lawrey
Ich finde das extrem überraschend ... soweit, dass ich meine Zweifel habe, dass Sie Messen die heap-Auslastung korrekt.
Lassen Sie uns davon ausgehen, dass Ihr code so etwas wie dieses:
Nun die Möglichkeit, dass ein ByteArrayOutputStream verwaltet den Puffer reservieren ist eine erste Größe, und (mindestens) die doppelte Puffer, wenn es füllt. Also, im schlimmsten Fall
baos
könnte bis zu 80Mb Puffer für eine 40Mb Datei.Den letzten Schritt stellt ein neues array mit genau
baos.size()
bytes zum speichern der Puffer - Inhalt. Das ist 40Mb. So ist die peak-Höhe der Speicher, der tatsächlich benutzt werden sollten 120Mb.Also wo sind die zusätzlichen 40 MB verwendet? Meine Vermutung ist, dass Sie nicht sind, und dass Sie eigentlich die Berichterstattung der gesamten heap-Größe, nicht die Menge des Speichers, der belegt wird durch die erreichbaren Objekte.
Also, was ist die Lösung?
Könnte man eine memory-mapped buffer.
Könnten Sie eine Größe Hinweis beim zuordnen der
ByteArrayOutputStream
; z.B.Könnten Sie verzichten auf die
ByteArrayOutputStream
ganz Lesen und direkt in ein byte-array.Beide Optionen 1 und 2 sollten eine maximale Speichernutzung von 40 MB beim Lesen 40Mb-Datei; d.h. kein vergeudeter Platz.
Wäre es hilfreich, wenn Sie Ihren code gepostet, und beschrieb Ihre Methode für die Messung der Speicherauslastung.
Die potenzielle Gefahr ist, dass Ihre Annahmen falsch sind, oder werden falsch, weil jemand anderes ändern Sie Ihren code unwissentlich ...
InformationsquelleAutor der Antwort Stephen C
Sollte man nicht ändern, das spezifizierte Verhalten der bestehenden Methode, aber es ist völlig in Ordnung, um eine neue Methode hinzufügen. Hier ist eine Implementierung:
Alternative, sondern hackish Weg, um den Puffer aus alle ByteArrayOutputStream ist die Verwendung der Tatsache, dass seine
writecontentto(Ausgabestrom)
- Methode übergibt den Puffer direkt in den Ausgabestrom zur Verfügung gestellt:(Das funktioniert, aber ich bin mir nicht sicher, ob es nützlich ist, gegeben, dass Unterklassen ByteArrayOutputStream ist einfacher.)
Jedoch von dem rest Ihrer Frage, es klingt wie alles, was Sie wollen ist eine einfache
byte[]
der komplette Inhalt der Datei. Ab Java 7, der einfachste und Schnellste Weg zu tun, der ist, rufen- Dateien.readAllBytes
. In Java 6 und darunter, können SieDataInputStream.readFully
wie in Peter Lawrey Antwort. Jede Weise, erhalten Sie ein array zugewiesen wird einmal in der richtigen Größe, ohne die wiederholte Umverteilung der ByteArrayOutputStream.InformationsquelleAutor der Antwort Boann
Google Guava ByteSource scheint eine gute Wahl für die Pufferung im Speicher. Im Gegensatz zu Implementierungen wie
ByteArrayOutputStream
oderByteArrayList
(von Colt-Bibliothek) nicht Zusammenführen der Daten in einem großen byte-array speichert aber jedes Stück separat. Ein Beispiel:Den
ByteSource
kann gelesen werden als eineInputStream
jederzeit später:InformationsquelleAutor der Antwort 30thh
... kam hier mit die gleiche Beobachtung beim Lesen einer 1 GB-Datei: Oracle ByteArrayOutputStream hat ein lazy-memory-management.
Ein byte-Array wird indiziert durch eine int-und eine solche sowieso auf 2 GB beschränkt. Ohne Abhängigkeit von 3rd-party-vielleicht finden Sie diese nützlich:
InformationsquelleAutor der Antwort Sam Ginrich