Es hat von Java 1.6 nach Java 1.7 eine für den Memoryverbrauch relevante Änderung gegeben.
Bis Java 1.6 wurde bei substring
eine Zeichenkette konstruiert, die das selbe interne Array von Zeichen (char) referenziert und nur andere Werte für Anfang und Länge enthält. Das hatte den Vorteil, dass in vielen Fällen Memory gespart wurde, weil man die Teilzeichenkette nicht nochmal speichern musste. Auch das Umkopieren wurde gespart. Nun war es aber wichtig, in manchen Fällen so etwas wie
String mySubString = new String(origString.substring(3, 5));
statt
String mySubString = origString.substring(3, 5);
zu verwenden, wenn nämlich origString sehr viel länger als mySubString war und sehr viel früher obsolet wurde als mySubString. Wer das vergessen hat, hat so ein Memoryleak gebaut.
Das wurde in Java 1.7 „gefixt“, nun wird das Array mit den Zeichen intern immer kopiert, weil die Entwickler nicht verstanden haben, mit diesen beiden Möglichkeiten richtig umzugehen. Leider hat man nun aber die andere Möglichkeit nicht mehr zur Verfügung.
Was kann man tun, wenn man eine Memory-intensive Applikation schreiben will, in der viele langlebige Zeichenketten vorkommen, die Teil einer längeren, ebenfalls langlebigen Zeichenkette sind?
Die erste Frage ist immer, ob man mit der neue Implementierung nicht doch leben kann.
Da die Zeichenkette sehr tief in Java verankert ist, kann man sie nicht so leicht ersetzen bzw. muss etwas hässlicheren Code schreiben, um explizit die eigene Zeichenkette zu verwenden. Wenn nötig ist das aber machbar: Man nimmt von gnuClasspath java.lang.String und refactored das zu einem anderen Package- und Klassennamen. Daraus lässt sich dann leicht das gewünschte bauen. Wichtig sind Konversionsroutinen nach java.lang.String in beide Richtungen.
Links: