Sie können häufig Diskussionen über die Speicherzuweisung von Java -Zeichenfolgen sehen, wenn Sie in den wichtigsten Abschnitten im Internet ausgeführt werden, wie z. Tatsächlich wird der wörtliche Wert von String "123" in diesen beiden Formen weder auf dem Stapel noch auf dem Haufen zur Laufzeit gespeichert. Sie werden in einem bestimmten konstanten Bereich im Methodenbereich gespeichert, und nur eine Kopie wird im Speicher für denselben Literalwert der Zeichenfolge aufbewahrt. Wir werden es mit Beispielen unten analysieren.
1. Der == Operator wird in zwei Fällen verwendet, in denen zwei String -Referenzvergleiche verglichen werden:
public class StringTest {public static void main (String [] args) {// Teil 1 String s1 = "Ich liebe China"; String S2 = "Ich liebe China"; System.out.println ("Ergebnis:" + S1 == S2); // Das Programmlaufergebnis ist wahr // Teil 2 String s3 = neuer String ("I Love China"); String S4 = New String ("Ich liebe China"); System.out.println ("Ergebnis:" + S3 == S4); // Das Programmlaufergebnis ist falsch}}Wir wissen, dass der == Operator in Java die Werte von Variablen vergleicht. Der Wert der Variablen, die dem Referenztyp entspricht, speichert die Adresse des Referenzobjekts. Hier ist String der Referenztyp und die Werte der vier Variablen hier speichern tatsächlich die Adressen der Zeichenfolge. Das Ausführungsergebnis für Part2 ist offensichtlich, da der neue Bediener dazu führt, dass JVM zur Laufzeit neue Objekte auf dem Haufen erstellt und die Adressen der beiden verschiedenen Objekte unterschiedlich sind. Aus dem Ausführungsergebnis von Teil1 ist jedoch ersichtlich, dass S1 und S2 dieselbe Adresse sind, auf die angezeigt wird. Woher werden die Saiten, auf die die Variablen S1 und S2 gespeichert sind? Wie geht JVM mit Strings um? In ähnlicher Weise werden für verschiedene String -Objekte im Heap, auf die Variablen S3 und S4 hingewiesen wurden, in ihrem eigenen Objektraum eine "I Love China" -Skette retten? Um zu verstehen, wie JVM Saiten umgeht, betrachten wir zunächst die vom Java -Compiler erzeugten Bytecode -Anweisungen. Durch die Bytecode -Anweisung analysieren wir, welche Operationen von JVM ausgeführt werden.
2. Die folgenden Informationen finden Sie einige vom Programm generierte Bytecode -Informationen. Der rote markiert den Teil, auf den wir achten müssen.
Konstante Pool: #1 = Klasse #2 // Stringest #2 = utf8 StringTest #3 = Klasse #4 // Java/Lang/Objekt #4 = UTF8 Java/Lang/Objekt #5 = utf8 <init> #6 = utf8 () V #7 = utf8 Code #8 = methodref #3. #9 // Java/Lang/Lang/Lang/Lang/Lang/Lang/Lang/Lang/Lang/Lang/Lang/Lang/Lang/Lang/Lang/Lang. #5: #6 // "<init>" :() v #10 = utf8 linenumbertable #11 = utf8 localVariabletable #12 = utf8 this #13 = utf8 lstringtest; #14 = utf8 main #15 = utf8 ([ljava/lang/string;) v #16 = String #17 // Ich liebe China Referenz auf String -Adresse #17 = utf8 Ich liebe China #18 = FieldRef #19. #21 // Java/Lang/System.out: ljava/io/printream; #19 = Klasse #20 // Java/Lang/System #20 = UTF8 Java/Lang/System #21 = nameandType #22: #23 // out: ljava/io/printstream; #22 = UTF8 OUT #23 = UTF8 LJAVA/IO/PRINTSTREAM; #24 = Klasse #25 // Java/lang/StringBuilder #25 = UTF8 Java/Lang/StringBuilder #26 = String #27 // Ergebnis: #27 = Utf8 Ergebnis: #28 = methodref #24. #29 // java/lang/stringbuilder. "<init>" :( ljava/lang/string;) v#30 = utf8 (ljava/lang/string;) v#31 = methodref#24.#32 // java/lang/stringBuilder.append: (z) ljava/lang/stringbuilder;#32 = namenDtype#33:#34 /// append:(Z)Ljava/lang/StringBuilder;#33 = Utf8 append#34 = Utf8 (Z)Ljava/lang/StringBuilder;#35 = Methodref #24.#36 // java/lang/StringBuilder.toString:()Ljava/lang/String;#36 = NameAndType #37:#38 // toString :() ljava/lang/string;#37 = utf8 toString#38 = utf8 () ljava/Lang/String;#39 = methodref#40.#42 // java/io/printstream.println: (ljava/lang/string;) v#40 = 41 //0 = 41/11 //java/lang/lang/String;) v#40 = 41 // // java/io/printstream#42 = nameandtype#43:#30 // println: (ljava/lang/string;) v#43 = utf8 println#44 = Klasse#45 // Java/Lang/String#45 = utf8 Java/lang/String#46 = methodref#44.#29 //lang/String#46 = methodref#44.#29 //lang/lang/String#46 = methodref#44.#29 //lang/lang/String#46 = methodref#44.#29 // Lang/String#46 = methodref#44.#29 //. java/lang/string. "<init>" :( ljava/lang/string;) v#47 = utf8 args#48 = utf8 [ljava/lang/string;#49 = utf8 S1#50 = UTF8 LJAVA/LANG/SING; StackMaptable#55 = Klasse#48 // "[ljava/lang/string;"#56 = utf8 SourceFile#57 = utf8 stringest.java ............ // Die Bytecode -Anweisung für die entsprechende Methode wird von der JVM -Laufzeit erläutert und ausgeführt. Public Static Void Main (Java.lang.String []); Deskriptor: ([Ljava/Lang/String;) V Flags: ACC_PUBLIC, ACC_STATISCHE CODE: STACK = 4, LOCALS = 5, Args_Size = 1 0: LDC #16 // String Ich liebe China, diese Richtlinie raffiniert sich auf das Symbol des Symbols des Stapels. Diese Anweisung entspricht der folgenden Anweisung 2. String S1 = "Ich liebe China" Aussage 2 Im Programm: Store_1 // Die Objektreferenz oben im Stapel der lokalen Variablen zuweisen. Diese Anweisung und die folgende Anweisung 5 entsprechen der Aussage S2 = "Ich liebe China" im Programm. 5: Store_2 // Objektreferenzen lokalen Variablen oben im Stapel zuweisen 2. 6: GetStatic #18 // Feld Java/Lang/System.out: ljava/io/printstream; 9: Neu #24 // Klasse Java/Lang/StringBuilder 12: DUP 13: LDC #26 // String Ergebnis: 15: Invokespecial #28 // Methode Java/Lang/StringBuilder. Ob sie gleich sind oder nicht, gehen Sie zur Richtlinie 27, führen Sie den nächsten Anweisungen gleich 23 aus 23: iconst_1 24: Goto 28 27: iconst_0 28: invokevirtual #31 // method java/lang/stringBuilder.append: (z) ljava/lang/stringbuilder; 31: InvokeVirtual #35 // Methode Java/Lang/StringBuilder.toString :() Ljava/Lang/String; 34: InvokeVirtual #39 // Methode Java/io/printstream.println: (ljava/lang/string;) v 37: neu #44 // Klasse Java/Lang/String, erstellen Sie ein Objekt, das sich am Referenz des konstanten Pools #44 befindet. 40: DUP // Kopieren Sie eine Kopie des Objekts oben im Stapel und schieben Sie es an die Oberseite des Stapels. 41: LDC #16 // String Ich liebe China, gilt wie 0, 3 Anweisungen. 43: Invokespecial #46 // Methode Java/Lang/String. 53: Invokespecial #46 // Methode Java/Lang/String. java/lang/system.out: ljava/io/printstream; 61: NEU #24 // Klasse Java/Lang/StringBuilder 64: DUP 65: LDC #26 // String -Ergebnis: 67: invokespecial #28 // method java/lang/stringBuilder.append: (z) ljava/lang/stringbuilder; 84: InvokeVirtual #35 // Methode Java/Lang/StringBuilder.toString :() Ljava/Lang/StringBuilder.Append: (z) ljava/lang/StringBuilder; 84: InvokeVirtual #35 // Methode Java/Lang/StringBuilder.toString :() Ljava/Lang/StringBuilder; 87: InvokeVirtual #39 // Methode Java/io/printstream.println: (ljava/lang/string;) v 90: return ......... linenumbertable: Zeile 7: 0line 8: 3line 9: 6line 11: 37line 12: 47line [Ljava/Lang/String; // Lokale Variable 088 1 S1 Ljava/Lang/String; // Lokale Variable 185 2 S2 Ljava/Lang/String; // Lokale Variable 244 3 S3 Ljava/Lang/String; // Lokale Variable 333 4 S4 Ljava/Lang/String; // Lokale Variable 4
Der rote Teil des Bytecode bezieht sich auf unsere Diskussion. Durch den generierten Bytecode können wir die folgenden Schlussfolgerungen für das Beispielprogramm ziehen.
1). Wenn der Java -Compiler das Programm in Bytecode kompiliert, bestimmt die String Constant "I Love China" zuerst, ob es im Bytecode Constant Pool vorliegt. Wenn es nicht existiert, wird es nicht erstellt. Das heißt, eine gleiche Zeichenfolge, kann nur eine Kopie beibehalten werden. Es kann durch symbolische Referenzen gefunden werden, so dass die String -Variablen S1 und S2 im Programm auf die gleiche Zeichenfolgekonstante im konstanten Pool hinweisen. Zur Laufzeit speichert JVM Stringkonstanten im Bytecode Constant Pool am Ort des Methodenbereichs, der üblicherweise als konstantes Pool bezeichnet wird, und auf die Zeichenfolge wird durch Indizes in Form eines Zeichenarrays zugegriffen. JVM zeigt die relative Referenzadresse der Zeichenfolge, auf die S1 und S2 zur tatsächlichen Speicheradresse der String zur Laufzeit verweist.
2). Für String S3 = New String ("I Love China"), String S4 = New String ("I Love China"), aus dem Bytecode ist zu sehen, dass es die neue Anweisung nennt. JVM erstellt zwei verschiedene Objekte zur Laufzeit, und S3 und S4 verweisen auf verschiedene Objektadressen. Daher ist das Ergebnis des S3 == S4 -Vergleichs falsch.
Zweitens wird für die Initialisierung von S3- und S4 -Objekten aus dem Bytecode ersichtlich, dass die Init -Methode des Objekts aufgerufen wird und die Referenz "I Love China" im konstanten Pool übergeben wird. Was ist die Erstellung des String -Objekts und der Initialisierung? Wir können den Quellcode der Zeichenfolge und den vom String -Objekt generierten Bytecode überprüfen, um besser zu verstehen, ob die Zeichenfolge innerhalb des Objekts kopiert wird, oder in der Referenz direkt auf die Adresse des konstanten Pools, auf die der Zeichenfolge entspricht, wird darauf hingewiesen.
3. Teil des Quellcode des String -Objekts:
<Span style = "Schriftgröße: 14PT"> öffentliche endgültige Klasse-String implementiert java.io.serializable, vergleichbar <string>, charsequence { /** Der Wert wird für den Zeichenspeicher verwendet. */ privater endgültiger Charwert []; / ** zwischen dem Hash -Code für die Zeichenfolge*/ private int Hash; // standardmäßig 0 public string () {this.Value = new char [0]; } </Span> <span style = "background-color: #ffffff; Schriftgröße: 18PT"> public String (String Original) {this.Value = original.Value; this.hash = original.hash; } </Span>Aus dem Quellcode sehen wir, dass es in der String -Klasse einen Instanzvariablen -Zeichen -Wert [] gibt. Durch die Konstruktionsmethode können wir feststellen, dass das Objekt beim Initialisieren keine Kopiervorgänge durchführt, sondern nur die Adressreferenz des bestandenen String -Objekts dem Instanzvariablenwert zuweist. Aus diesem Grund können wir zunächst zu dem Schluss kommen, dass selbst wenn ein String -Objekt mit New String ("ABC") erstellt wird, der Speicherplatz für das Objekt im Speicherhaufen zugewiesen wird, aber keine Informationen über "ABC" selbst werden auf dem Heap gespeichert, aber der Verweis auf die "ABC" -Sache wird in seiner Instanzvariablen in die "ABC" -Skette initialisiert. In der Tat soll auch Speicherplatz Speicher Speicher speichern und die Programmleistung verbessert werden.
4. Schauen wir uns die Bytecode -Informationen des String -Objekts an:
Public Java.lang.String (); Deskriptor: () V Flags: ACC_PUBLIC CODE: STACK = 2, LOCALS = 1, Args_Size = 1 0: ALOAD_0 1: Invokespecial #1 // Methode Java/Lang/Objekt. 138: 4 Zeile 139: 11 Public Java.lang.String (Java.lang.String); Deskriptor: (Ljava/Lang/String;) V Flags: ACC_PUBLIC CODE: STACK = 2, LOCALS = 2, Args_Size = 2 0: ALOAD_0 // Drücken Sie die lokale Variable 0 an die Spitze des Stapels, eine Referenz auf sein eigenes Objekt. 1: Invokespecial #1 // Methode Java/Lang/Object. 4: ALOAD_0 // Drücken Sie die Referenz seines eigenen Objekts an die Oberseite des Stapels. 5: ALOAD_1 // Die übergebene String -Referenz drückt nach oben im Stapel. 6: GetField #2 // Feldwert: [C // Die String -Referenz oben im Stapel aufpopieren und der Instanzvariablen bei #2 zuweisen und auf dem Stapel speichern. 9: Putfield #2 // Feldwert: [C // Die String -Referenz oben im Stapel und das Objekt selbst aufpopieren und die String -Referenz auf die Instanzvariable des Objekts selbst zuweisen. 12: ALOAD_0 13: ALOAD_1 14: GetField #3 // Field Hash: I 17: Putfield #3 // Field Hash: I 20: Return
Aus der Perspektive von Bytecode können wir schließen, dass die neue String ("ABC") Zuordnungen von String -Referenzen beim Erstellen eines neuen Objekts ausführt, anstatt Strings zu kopieren. Das obige ist eine Analyse und Zusammenfassung der Speicherzuweisung von Zeichenfolgen aus der Perspektive des Quellcode und des Byte -Codes.
Die obige Analyse und Zusammenfassung der Java -String -Speicherzuweisung (empfohlen) ist der gesamte Inhalt, den ich mit Ihnen teile. Ich hoffe, Sie können Ihnen eine Referenz geben und ich hoffe, Sie können wulin.com mehr unterstützen.