Ich habe beispielsweise eine Variable mit vielen Daten verwendet String data
. Ich wollte einen kleinen Teil dieser Zeichenfolge folgendermaßen verwenden:
this.smallpart = data.substring(12,18);
Nach einigen Stunden des Debuggens (mit einem Speichervisualisierer) stellte ich fest, dass sich das smallpart
Objektfeld alle Daten von erinnerte data
, obwohl es nur die Teilzeichenfolge enthielt.
Als ich den Code geändert habe in:
this.smallpart = data.substring(12,18)+"";
..das Problem wurde gelöst! Jetzt verbraucht meine Anwendung nur noch sehr wenig Speicher!
Wie ist das möglich? Kann jemand das erklären? Ich denke, this.smallpart bezog sich immer wieder auf Daten, aber warum?
UPDATE: Wie kann ich dann den großen String löschen? Wird data = new String (data.substring (0,100)) das tun?
java
performance
string
memory
hsmit
quelle
quelle
new String(String)
. Siehe stackoverflow.com/a/390854/8946 .Antworten:
Folgendes tun:
Erstellt ein neues (kleineres) String-Objekt und wirft den Verweis auf den von substring () erstellten String weg, wodurch die Speicherbereinigung dieses Objekts ermöglicht wird.
Es ist wichtig zu wissen, dass
substring()
ein Fenster zu einem vorhandenen String angezeigt wird - oder vielmehr zu dem Zeichenarray, das dem ursprünglichen String zugrunde liegt. Daher wird derselbe Speicher wie der ursprüngliche String belegt. Dies kann unter bestimmten Umständen vorteilhaft sein, ist jedoch problematisch, wenn Sie einen Teilstring erhalten und den ursprünglichen String entsorgen möchten (wie Sie herausgefunden haben).Werfen Sie einen Blick auf die Methode substring () in der JDK String Quelle für weitere Informationen.
BEARBEITEN: Um Ihre Zusatzfrage zu beantworten, wird durch das Erstellen eines neuen Strings aus dem Teilstring der Speicherverbrauch reduziert, sofern Sie keine Verweise auf den ursprünglichen String enthalten.
HINWEIS (Januar 2013). Das obige Verhalten hat sich in Java 7u6 geändert . Das Fliegengewichtsmuster wird nicht mehr verwendet und
substring()
funktioniert wie erwartet.quelle
String(String)
Konstruktor (dh der String-Konstruktor, der einen String als Eingabe verwendet) nützlich ist: Ernew String(data.substring(x, y))
macht effektiv dasselbe wie das Anhängen""
, macht jedoch die Absicht etwas klarer.value
Attribut des ursprünglichen Strings. Ich denke, deshalb wird die Referenz beibehalten.Wenn Sie sich die Quelle von ansehen
substring(int, int)
, werden Sie feststellen, dass sie zurückkehrt:Wo
value
ist das Originalchar[]
? Sie erhalten also einen neuen String mit demselben Basiswertchar[]
.Wenn Sie dies tun, erhalten
data.substring() + ""
Sie einen neuen String mit einem neuen Basiswertchar[]
.Tatsächlich ist Ihr Anwendungsfall die einzige Situation, in der Sie den
String(String)
Konstruktor verwenden sollten:quelle
new String(String)
. Siehe stackoverflow.com/a/390854/8946 .Wenn Sie verwenden
substring
, wird keine neue Zeichenfolge erstellt. Es bezieht sich immer noch auf Ihre ursprüngliche Zeichenfolge mit einer Versatz- und Größenbeschränkung.Damit Ihre ursprüngliche Zeichenfolge erfasst werden kann, müssen Sie eine neue Zeichenfolge erstellen (mit
new String
oder was Sie haben).quelle
Da Java-Zeichenfolgen aus einem char-Array, einem Startoffset und einer Länge (und einem zwischengespeicherten Hashcode) bestehen. Einige String-Operationen wie das
substring()
Erstellen eines neuen String-Objekts, das das char-Array des Originals gemeinsam nutzt und einfach unterschiedliche Offset- und / oder Längenfelder aufweist. Dies funktioniert, weil das char-Array eines Strings nach seiner Erstellung niemals geändert wird.Dies kann Speicherplatz sparen, wenn viele Teilzeichenfolgen auf dieselbe Basiszeichenfolge verweisen, ohne überlappende Teile zu replizieren. Wie Sie bemerkt haben, kann es in einigen Situationen verhindern, dass Daten, die nicht mehr benötigt werden, durch Müll gesammelt werden.
Der "richtige" Weg, dies zu beheben, ist der
new String(String)
Konstruktor, dhÜbrigens wäre die insgesamt beste Lösung, zu vermeiden, dass überhaupt sehr große Strings vorhanden sind, und Eingaben in kleineren Blöcken zu verarbeiten, jeweils einige KB.
quelle
new String(String)
. Siehe stackoverflow.com/a/390854/8946 .In Java sind Zeichenfolgen unveränderliche Objekte. Sobald eine Zeichenfolge erstellt wurde, bleibt sie im Speicher, bis sie vom Garbage Colector bereinigt wird (und diese Bereinigung ist nicht selbstverständlich).
Wenn Sie die Teilstring-Methode aufrufen, erstellt Java keine völlig neue Zeichenfolge, sondern speichert lediglich einen Zeichenbereich in der ursprünglichen Zeichenfolge.
Wenn Sie also eine neue Zeichenfolge mit diesem Code erstellt haben:
Sie haben tatsächlich eine neue Zeichenfolge erstellt, als Sie das Ergebnis mit der leeren Zeichenfolge verkettet haben. Deshalb.
quelle
Wie von jwz 1997 dokumentiert :
quelle
Um es zusammenzufassen: Wenn Sie viele Teilzeichenfolgen aus einer kleinen Anzahl großer Zeichenfolgen erstellen, verwenden Sie
Da Sie nur den Speicherplatz zum Speichern der großen Zeichenfolgen verwenden, aber wenn Sie nur eine Handvoll kleiner Zeichenfolgen aus verlorenen großen Zeichenfolgen extrahieren, dann
Hält Ihren Speicherbedarf niedrig, da die großen Zeichenfolgen zurückgefordert werden können, wenn sie nicht mehr benötigt werden.
Das, was Sie aufrufen,
new String
ist eine hilfreiche Erinnerung daran, dass Sie wirklich eine neue Zeichenfolge erhalten, anstatt auf die ursprüngliche zu verweisen.quelle
new String(String)
. Siehe stackoverflow.com/a/390854/8946 .Erstens erstellt der Aufruf
java.lang.String.substring
ein neues Fenster im OriginalString
unter Verwendung des Versatzes und der Länge, anstatt den wesentlichen Teil des zugrunde liegenden Arrays zu kopieren.Wenn wir uns die
substring
Methode genauer ansehen, werden wir einen String-Konstruktor- Aufruf bemerkenString(int, int, char[])
und ihn als Ganzes übergebenchar[]
, der den String darstellt . Das bedeutet, dass der Teilstring so viel Speicher belegt wie die ursprüngliche Zeichenfolge .Ok, aber warum
+ ""
wird weniger Speicher benötigt als ohne?Ein
+
Onstrings
wird über einenStringBuilder.append
Methodenaufruf implementiert . Wenn Sie sich die Implementierung dieser Methode in derAbstractStringBuilder
Klasse ansehen, werden Sie feststellen, dass sie endlicharraycopy
mit dem Teil zu tun hat, den wir gerade wirklich brauchen (dersubstring
).Irgendeine andere Problemumgehung?
quelle
Das Anhängen von "" an eine Zeichenfolge spart manchmal Speicher.
Angenommen, ich habe eine riesige Zeichenfolge mit einem ganzen Buch, einer Million Zeichen.
Dann erstelle ich 20 Zeichenfolgen, die die Kapitel des Buches als Teilzeichenfolgen enthalten.
Dann erstelle ich 1000 Zeichenfolgen, die alle Absätze enthalten.
Dann erstelle ich 10.000 Zeichenfolgen, die alle Sätze enthalten.
Dann erstelle ich 100.000 Zeichenfolgen, die alle Wörter enthalten.
Ich benutze immer noch nur 1.000.000 Zeichen. Wenn Sie jedem Kapitel, Absatz, Satz und Wort "" hinzufügen, verwenden Sie 5.000.000 Zeichen.
Natürlich ist es ganz anders, wenn Sie nur ein einziges Wort aus dem ganzen Buch extrahieren, und das ganze Buch könnte Müll gesammelt werden, aber nicht, weil dieses eine Wort einen Verweis darauf enthält.
Und es ist wieder anders, wenn Sie eine Million Zeichenfolgen haben und Tabulatoren und Leerzeichen an beiden Enden entfernen, z. B. 10 Aufrufe, um eine Teilzeichenfolge zu erstellen. Durch die Funktionsweise oder Funktionsweise von Java wird vermieden, dass jedes Mal eine Million Zeichen kopiert werden. Es gibt Kompromisse, und es ist gut, wenn Sie wissen, was die Kompromisse sind.
quelle