Der Javadoc über String.intern()
gibt nicht viele Details. (Kurz gesagt: Es wird eine kanonische Darstellung der Zeichenfolge zurückgegeben, sodass internierte Zeichenfolgen mit verglichen werden können. ==
)
- Wann würde ich diese Funktion zugunsten von nutzen
String.equals()
? - Gibt es Nebenwirkungen, die im Javadoc nicht erwähnt werden, dh mehr oder weniger Optimierung durch den JIT-Compiler?
- Gibt es weitere Verwendungen von
String.intern()
?
Antworten:
wenn Sie Geschwindigkeit benötigen, da Sie Zeichenfolgen anhand der Referenz vergleichen können (== ist schneller als gleich)
Der Hauptnachteil ist, dass Sie daran denken müssen, sicherzustellen, dass Sie tatsächlich alle Zeichenfolgen intern () ausführen, die Sie vergleichen möchten. Es ist leicht zu vergessen, alle Zeichenfolgen zu internieren (), und dann können Sie verwirrend falsche Ergebnisse erhalten. Stellen Sie außerdem für alle sicher, dass Sie sehr deutlich dokumentieren, dass Sie sich auf die zu verinnerlichenden Zeichenfolgen verlassen.
Der zweite Nachteil bei der Internalisierung von Zeichenfolgen besteht darin, dass die intern () -Methode relativ teuer ist. Es muss den Pool eindeutiger Zeichenfolgen verwalten, damit es ein gutes Stück Arbeit erledigt (auch wenn die Zeichenfolge bereits verinnerlicht wurde). Seien Sie also bei Ihrem Code-Design vorsichtig, damit Sie z. B. alle geeigneten Zeichenfolgen bei der Eingabe intern () verwenden, damit Sie sich nicht mehr darum kümmern müssen.
(von JGuru)
Dritter Nachteil (nur Java 7 oder weniger): Internierte Strings leben im PermGen-Raum, der normalerweise recht klein ist. Möglicherweise stoßen Sie auf einen OutOfMemoryError mit viel freiem Heap-Speicherplatz.
(von Michael Borgwardt)
quelle
if (s1.equals(s2))
undif (i1 == i2)
ist minimal, es sei denn, Sie haben viele lange Zeichenfolgen mit denselben Hauptfiguren. In den meisten realen Anwendungen (außer URLs) unterscheiden sich die Zeichenfolgen innerhalb der ersten Zeichen. Und lange If-else-Ketten riechen sowieso nach Code: Verwenden Sie Aufzählungen und Funktionskarten.Dies hat (fast) nichts mit einem String-Vergleich zu tun. Das Internieren von Zeichenfolgen dient zum Speichern von Speicher, wenn Ihre Anwendung viele Zeichenfolgen mit demselben Inhalt enthält. Wenn Sie
String.intern()
die Anwendung verwenden, wird dies auf lange Sicht nur eine Instanz haben. Ein Nebeneffekt ist, dass Sie einen schnellen Referenzgleichheitsvergleich anstelle eines normalen Zeichenfolgenvergleichs durchführen können (dies ist jedoch normalerweise nicht ratsam, da es wirklich leicht zu brechen ist, wenn Sie nur den Praktikanten vergessen eine einzelne Instanz).quelle
str.intern()
wennstr
ist"Hello"
.String.intern()
ist definitiv Müll, der in modernen JVMs gesammelt wird.Aufgrund der GC-Aktivität geht NIEMALS der Speicherplatz aus:
Sehen Sie mehr (von mir) über den Mythos von nicht GCed String.intern () .
quelle
OutOfMemoryException
- Nein, nicht der obige Code in meinem Gehirn : Link zum Javaturning-Artikel, der auf diesen Artikel verweist, der auf den Javaturning-Artikel verweist, der ... :-)Ich habe kürzlich einen Artikel über die Implementierung von String.intern () in Java 6, 7 und 8 geschrieben: String.intern in Java 6, 7 und 8 - String Pooling .
Ich hoffe, es sollte genügend Informationen über die aktuelle Situation mit String-Pooling in Java enthalten.
In einer Nussschale:
String.intern()
in Java 6, da es in PermGen gehtString.intern()
in Java 7 und Java 8: Es verbraucht 4-5x weniger Speicher als das Rollen Ihres eigenen Objektpools-XX:StringTableSize
(die Standardeinstellung ist wahrscheinlich zu klein; stellen Sie eine Primzahl ein).quelle
Der Vergleich von Strings mit == ist viel schneller als mit equals ()
5 Zeit schneller, aber da der String-Vergleich normalerweise nur einen kleinen Prozentsatz der gesamten Ausführungszeit einer Anwendung ausmacht, ist der Gesamtgewinn viel geringer und der endgültige Gewinn wird auf einige Prozent verwässert.
String.intern () zieht den String vom Heap weg und legt ihn in PermGen ab
Verinnerlichte Zeichenfolgen werden in einem anderen Speicherbereich abgelegt: Permanente Generierung. Dies ist ein Bereich der JVM, der für Nichtbenutzerobjekte wie Klassen, Methoden und andere interne JVM-Objekte reserviert ist. Die Größe dieses Gebiets ist begrenzt und das ist viel wertvoller als Haufen. Da dieser Bereich kleiner als Heap ist, besteht eine höhere Wahrscheinlichkeit, dass der gesamte Speicherplatz genutzt wird und eine OutOfMemoryException ausgelöst wird.
String.intern () String sind Müll gesammelt
In den neuen Versionen von JVM werden auch internalisierte Zeichenfolgen durch Müll gesammelt, wenn sie von keinem Objekt referenziert werden.
Wenn Sie die obigen 3 Punkte berücksichtigen, können Sie ableiten, dass String intern () nur in wenigen Situationen nützlich sein kann, wenn Sie viele Zeichenfolgenvergleiche durchführen. Es ist jedoch besser, keine interne Zeichenfolge zu verwenden, wenn Sie nicht genau wissen, was Sie sind sind dabei ...
quelle
Angesichts der Tatsache, dass sie verschiedene Dinge tun, wahrscheinlich nie.
Das Internieren von Zeichenfolgen aus Leistungsgründen, damit Sie sie auf Referenzgleichheit vergleichen können, ist nur dann von Vorteil, wenn Sie eine Weile auf die Zeichenfolgen verweisen - Zeichenfolgen, die aus Benutzereingaben oder E / A stammen, werden nicht interniert.
Das bedeutet, dass Sie in Ihrer Anwendung Eingaben von einer externen Quelle erhalten und zu einem Objekt verarbeiten, das einen semantischen Wert hat - beispielsweise einen Bezeichner -, aber dieses Objekt hat einen Typ, der nicht von den Rohdaten zu unterscheiden ist, und unterschiedliche Regeln, wie der Programmierer vorgehen soll benutze es.
Es ist fast immer besser, einen
UserId
Typ zu erstellen, der interniert ist (es ist einfach, einen thread-sicheren generischen Internierungsmechanismus zu erstellen) und sich wie eine offene Aufzählung verhält, als denjava.lang.String
Typ mit Referenzsemantik zu überladen, wenn es sich zufällig um eine Benutzer-ID handelt.Auf diese Weise erhalten Sie keine Verwirrung darüber, ob ein bestimmter String interniert wurde oder nicht, und Sie können jedes zusätzliche Verhalten, das Sie benötigen, in die offene Aufzählung einkapseln.
quelle
Ich bin mir keiner Vorteile bewusst, und wenn es welche gäbe, würde man denken, dass equals () selbst intern () intern verwenden würde (was es nicht tut).
Busting intern () Mythen
quelle
intern
und sehr gute Gründe,equals
die dies standardmäßig nicht tun. Der Link, den Sie gepostet haben, ist ein kompletter Blödsinn. Der letzte Absatz gibt sogar zu, dassintern
es ein gültiges Verwendungsszenario gibt: schwere Textverarbeitung (z. B. ein Parser). Die Schlussfolgerung, dass „[XYZ] gefährlich ist, wenn Sie nicht wissen, was Sie tun“, ist so banal, dass es körperlich weh tut.Daniel Brückner hat absolut recht. String-Internierung soll Speicher (Heap) sparen. Unser System verfügt derzeit über eine riesige Hashmap zum Speichern bestimmter Daten. Wenn das System skaliert, ist die Hashmap groß genug, um den Heap aus dem Speicher zu machen (wie wir getestet haben). Durch das Internieren aller duplizierten Zeichenfolgen und aller Objekte in der Hashmap sparen wir eine erhebliche Menge an Heap-Speicherplatz.
Auch in Java 7 leben internierte Zeichenfolgen nicht mehr in PermGen, sondern in Heap. Sie müssen sich also keine Gedanken über die Größe machen und ja, es wird Müll gesammelt:
quelle
String
Instanzen verwendet wurde. Als ich mir ihren Inhalt ansah, sah ich viele Duplikate und entschied mich, zu wechselnintern()
, was Hunderte von MB sparte.Ich weiß nichts über die JIT-Ebene, aber es gibt eine direkte Bytecode-Unterstützung für den Zeichenfolgenpool , der magisch und effizient mit einer dedizierten
CONSTANT_String_info
Struktur implementiert wird (im Gegensatz zu den meisten anderen Objekten mit allgemeineren Darstellungen).JVMS
JVMS 7 5.1 sagt :
Bytecode
Es ist auch lehrreich, sich die Bytecode-Implementierung in OpenJDK 7 anzusehen.
Wenn wir dekompilieren:
Wir haben auf dem ständigen Pool:
und
main
:Beachten Sie, wie:
0
und3
: die gleicheldc #2
Konstante wird geladen (die Literale)12
: Eine neue String-Instanz wird erstellt (mit#2
als Argument)35
:a
undc
werden als reguläre Objekte mit verglichenif_acmpne
Die Darstellung konstanter Zeichenfolgen ist auf dem Bytecode ziemlich magisch:
new String
)und das obige JVMS-Zitat scheint zu sagen, dass immer dann, wenn der Utf8, auf den gezeigt wird, derselbe ist, identische Instanzen von geladen werden
ldc
.Ich habe ähnliche Tests für Felder durchgeführt und:
static final String s = "abc"
zeigt über das ConstantValue-Attribut auf die Konstantentabelleldc
Bonus : Vergleichen Sie das mit dem Integer-Pool , der keine direkte Bytecode-Unterstützung bietet (dh kein
CONSTANT_String_info
Analogon).quelle
Ich würde intern und == - Vergleich anstelle von gleich nur im Fall von Gleichheitsvergleich als Engpass bei mehreren Vergleichen von Zeichenfolgen untersuchen. Es ist sehr unwahrscheinlich, dass dies bei einer geringen Anzahl von Vergleichen hilft, da intern () nicht kostenlos ist. Nach dem aggressiven Internieren von Zeichenfolgen werden Aufrufe von intern () immer langsamer.
quelle
Eine Art Speicherverlust kann durch die Verwendung entstehen,
subString()
wenn das Ergebnis im Vergleich zur Quellzeichenfolge klein ist und das Objekt eine lange Lebensdauer hat.Die normale Lösung ist die Verwendung.
new String( s.subString(...))
Wenn Sie jedoch eine Klasse haben, die das Ergebnis eines potenziellen / wahrscheinlichensubString(...)
Ereignisses speichert und keine Kontrolle über den Aufrufer hat, können Sieintern()
die an den Konstruktor übergebenen String-Argumente speichern . Dies gibt den potentiell großen Puffer frei.quelle
Das Internieren von Zeichenfolgen ist nützlich, wenn die
equals()
Methode häufig aufgerufen wird, da dieequals()
Methode eine schnelle Überprüfung durchführt, um festzustellen, ob die Objekte zu Beginn der Methode identisch sind.Dies tritt normalerweise auf, wenn beim Durchsuchen eines
Collection
anderen Codes möglicherweise auch Zeichenfolgengleichheitsprüfungen durchgeführt werden.Das Internieren ist jedoch mit Kosten verbunden. Ich habe ein Mikrobenchmark für Code erstellt und festgestellt, dass der Internierungsprozess die Laufzeit um den Faktor 10 erhöht.
Der beste Ort für die Internierung ist normalerweise, wenn Sie Schlüssel lesen, die außerhalb des Codes gespeichert sind, da Zeichenfolgen im Code automatisch interniert werden. Dies geschieht normalerweise in der Initialisierungsphase Ihrer Anwendung, um die Strafe für den ersten Benutzer zu vermeiden.
Ein weiterer Ort, an dem dies möglich ist, ist die Verarbeitung von Benutzereingaben, die zum Durchführen von Schlüsselsuchen verwendet werden können. Dies geschieht normalerweise in Ihrem Anforderungsprozessor. Beachten Sie, dass die internierten Zeichenfolgen weitergegeben werden sollten.
Abgesehen davon macht es nicht viel Sinn, den Rest des Codes zu internieren, da dies im Allgemeinen keinen Nutzen bringt.
quelle
Ich würde dafür stimmen, dass es den Wartungsaufwand nicht wert ist.
In den meisten Fällen besteht keine Notwendigkeit und kein Leistungsvorteil, es sei denn, Ihr Code erledigt viel Arbeit mit Teilzeichenfolgen. In diesem Fall verwendet die String-Klasse die ursprüngliche Zeichenfolge plus einen Offset, um Speicherplatz zu sparen. Wenn Ihr Code häufig Teilzeichenfolgen verwendet, kann dies vermutlich dazu führen, dass Ihr Speicherbedarf explodiert.
quelle
http://kohlerm.blogspot.co.uk/2009/01/is-javalangstringintern-really-evil.html
behauptet, dass
String.equals()
verwendet wird"=="
, umString
Objekte vorher zu vergleichen , nachhttp://www.codeinstructions.com/2009/01/busting-javalangstringintern-myths.html
Es vergleicht die Länge der Strings und dann den Inhalt.
(Übrigens können Produktcode-Zeichenfolgen in einem Verkaufskatalog alle gleich lang sein - BIC0417 ist ein Schutzhelm für Radfahrer, TIG0003 ist ein lebender erwachsener männlicher Tiger - Sie benötigen wahrscheinlich alle Arten von Lizenzen, um eine davon zu bestellen Vielleicht bestellen Sie besser gleichzeitig einen Schutzhelm.)
Es hört sich also so an, als ob Sie einen Vorteil daraus ziehen, Ihre Strings durch ihre
intern()
Version zu ersetzen , aber Sie erhalten Sicherheit - und Lesbarkeit und Standardkonformität - ohne "==" fürequals()
in Ihrer Programmierung. Und das meiste, was ich sagen werde, hängt davon ab, ob es wahr ist, wenn es wahr ist.Aber testet
String.equals()
, dass Sie ihm einen String und kein anderes Objekt übergeben haben, bevor Sie ihn verwenden"=="
? Ich bin nicht qualifiziert zu sagen, aber ich würde nicht raten, da die meisten dieserequals()
Operationen überwiegend von String zu String ausgeführt werden, so dass der Test fast immer bestanden wird. In der TatString.equals()
impliziert die Priorisierung von "==" im Inneren die Gewissheit, dass Sie den String häufig mit demselben tatsächlichen Objekt vergleichen.Ich hoffe, niemand ist überrascht, dass die folgenden Zeilen ein Ergebnis von "false" ergeben:
Aber wenn Sie in die zweite Zeile wechseln
i
, isti.toString()
es natürlichtrue
.Zu den Orten, an denen Sie auf einen Nutzen aus dem Praktikum hoffen können, gehören
Set
undMap
natürlich. Ich hoffe, dass bei internierten Strings die Hashcodes zwischengespeichert sind ... Ich denke, das wäre eine Voraussetzung. Und ich hoffe, ich habe nicht nur eine Idee verschenkt, mit der ich eine Million Dollar verdienen könnte. :-)Was den Speicher betrifft, ist es auch offensichtlich, dass dies eine wichtige Grenze ist, wenn Ihr String-Volumen groß ist oder wenn Sie möchten, dass der von Ihrem Programmcode verwendete Speicher sehr klein ist. Wenn Ihr Volumen an -distinct- Strings sehr groß ist, ist es möglicherweise an der Zeit, dedizierten Datenbankprogrammcode für die Verwaltung und einen separaten Datenbankserver zu verwenden. Ebenso, wenn Sie ein kleines Programm verbessern können (das in 10000 Instanzen gleichzeitig ausgeführt werden muss), indem es seine Strings selbst überhaupt nicht speichert.
Es ist verschwenderisch, einen neuen String zu erstellen und ihn dann sofort als
intern()
Ersatz zu verwerfen , aber es gibt keine klare Alternative, außer den doppelten String beizubehalten. Die Ausführungskosten bestehen also darin, im internen Pool nach Ihrer Zeichenfolge zu suchen und dann dem Garbage Collector zu ermöglichen, das Original zu entsorgen. Und wenn es ein String-Literal ist, dann kommt es sowieso schon interniert.Ich frage mich, ob
intern()
durch bösartigen Programmcode missbraucht werden kann, um festzustellen, ob einige Zeichenfolgen und ihre Objektreferenzen bereits imintern()
Pool vorhanden sind und daher an anderer Stelle in der Java-Sitzung vorhanden sind, wenn dies nicht bekannt sein sollte. Aber das wäre nur möglich, wenn der Programmcode bereits vertrauensvoll verwendet wird, denke ich. Bei den Bibliotheken von Drittanbietern, die Sie in Ihr Programm aufnehmen, sollten Sie jedoch Ihre ATM-PIN-Nummern speichern und speichern!quelle
Der wahre Grund für die Verwendung von Praktikanten ist nicht der oben genannte. Sie können es verwenden, nachdem Sie einen Speicherfehler festgestellt haben. Viele der Zeichenfolgen in einem typischen Programm sind String.substring () einer anderen großen Zeichenfolge [Denken Sie daran, einen Benutzernamen aus einer 100K-XML-Datei zu entfernen. Die Java-Implementierung besteht darin, dass der Teilstring einen Verweis auf den ursprünglichen String und den Start + das Ende in diesem riesigen String enthält. (Der Gedanke dahinter ist eine Wiederverwendung derselben großen Saite)
Nach 1000 großen Dateien, von denen Sie nur 1000 Kurznamen speichern, behalten Sie die gesamten 1000 Dateien im Speicher! Lösung: Verwenden Sie in diesem Szenario einfach smallsubstring.intern ()
quelle
Ich verwende intern, um Speicher zu speichern. Ich habe eine große Menge an String-Daten im Speicher und bei der Verwendung von intern () wurde eine enorme Menge an Speicher gespeichert. Obwohl viel weniger Speicher verwendet wird, wird der verwendete Speicher leider im PermGen-Speicher und nicht im Heap gespeichert, und es ist schwierig, den Kunden zu erklären, wie die Zuweisung dieses Speichertyps erhöht werden kann.
Gibt es also eine Alternative zu intern (), um den Speicherverbrauch zu reduzieren (== versus Leistungsvorteile sind für mich kein Problem)?
quelle
Seien wir ehrlich: Das Hauptanwendungsszenario besteht darin, dass Sie einen Datenstrom (entweder über einen Eingabestream oder aus einem JDBC-Ergebnisset) lesen und es gibt eine Vielzahl kleiner Zeichenfolgen, die sich durchgehend wiederholen.
Hier ist ein kleiner Trick, mit dem Sie steuern können, mit welchem Mechanismus Sie Strings und andere unveränderliche Elemente verinnerlichen möchten, sowie eine Beispielimplementierung:
Ich benutze das oft, wenn ich Felder aus Streams oder aus ResultSets lese. Hinweis:
LRUCache
ist ein einfacher Cache, der auf basiertLinkedHashMap<K,V>
. Es ruft automatisch den vom Benutzer bereitgestellten anretrieve()
Methode für alle Cache-Fehler auf.Die Möglichkeit, dies zu verwenden, besteht darin,
LRUInternalizer
vor dem Lesen (oder vor dem Lesen) einen zu erstellen , damit Strings und andere kleine unveränderliche Objekte zu verinnerlichen und dann freizugeben. Beispielsweise:quelle
Ich benutze es, um den Inhalt von ungefähr 36000 Codes zwischenzuspeichern, die mit zugehörigen Namen verknüpft sind. Ich interniere die Zeichenfolgen im Cache, weil viele der Codes auf dieselbe Zeichenfolge verweisen.
Durch das Internieren der Zeichenfolgen in meinem Cache stelle ich sicher, dass Codes, die auf dieselbe Zeichenfolge verweisen, tatsächlich auf denselben Speicher verweisen, wodurch mir RAM-Speicherplatz gespart wird.
Wenn die internierten Strings tatsächlich Müll gesammelt würden, würde es für mich überhaupt nicht funktionieren. Dies würde den Zweck des Praktikums grundsätzlich zunichte machen. Meins wird nicht durch Müll gesammelt, da ich einen Verweis auf jede einzelne Zeichenfolge im Cache habe.
quelle
Die Kosten für die Internierung eines Strings sind viel höher als die Zeit, die bei einem Vergleich mit einem einzelnen stringA.equals (B) eingespart wird. Verwenden Sie es (aus Leistungsgründen) nur, wenn Sie wiederholt dieselben unveränderten Zeichenfolgenvariablen verwenden. Wenn Sie beispielsweise regelmäßig eine stabile Liste von Zeichenfolgen durchlaufen, um einige Karten zu aktualisieren, die im selben Zeichenfolgenfeld eingegeben wurden, können Sie eine schöne Speicherung erzielen.
Ich würde vorschlagen, String-Interning zu verwenden, um die Leistung zu optimieren, wenn Sie bestimmte Teile Ihres Codes optimieren.
Denken Sie auch daran, dass String unveränderlich sind und nicht den dummen Fehler machen
Denken Sie daran
quelle
Wenn Sie nach einem unbegrenzten Ersatz für String.intern suchen, auch Müll gesammelt, funktioniert das Folgende gut für mich.
Wenn Sie ungefähr abschätzen können, wie viele verschiedene Zeichenfolgen es geben wird, verwenden Sie einfach String.intern () mit -XX: StringTableSize = highEnoughValue .
quelle