Der Java-Konstantenpool ist ein Dauerthema und ein Favorit der Interviewer. Es gibt eine Vielzahl von Fragen zum Konstantenpool.
Empfehlung: Java-Video-Tutorial
Verteilung des virtuellen JVM-Speichers:
Der Programmzähler befindet sich dort, wo sich der JVM befindet führt das Programm aus. Die Pipeline speichert einige Sprunganweisungen. Dies ist zu fortgeschritten, als dass Xiao Cai es verstehen könnte.
Der lokale Methodenstapel ist der Stapel, den die JVM zum Aufrufen von Betriebssystemmethoden verwendet.
Der Stapel der virtuellen Maschine ist der Stapel, den JVM zum Ausführen von Java-Code verwendet.
Der Methodenbereich speichert einige Konstanten, statische Variablen, Klasseninformationen usw., die als Speicherort von Klassendateien im Speicher verstanden werden können.
Der Heap der virtuellen Maschine ist der Heap, den die JVM zum Ausführen von Java-Code verwendet.
Der Konstantenpool in Java ist tatsächlich in zwei Formen unterteilt: den statischen Konstantenpool und den Laufzeitkonstantenpool.
Der sogenannte statische Konstantenpool ist der Konstantenpool in der *.class-Datei. Der Konstantenpool in der Klassendatei enthält nicht nur Zeichenfolgenliterale (Zahlenliterale), sondern auch Klassen- und Methodeninformationen, die absolut belegen Kein Teil der Klassendatei. Der größte Teil des Speicherplatzes.
Was den Laufzeitkonstantenpool betrifft, so lädt die virtuelle JVM-Maschine den Konstantenpool in der Klassendatei in den Speicher und speichert ihn im Methodenbereich. Wir nennen ihn oft die Konstante Pool bezieht sich auf den Laufzeitkonstantenpool im Methodenbereich.
Als nächstes zitieren wir einige beliebte Beispiele für konstante Pools im Internet und verwenden sie dann zur Erläuterung.
String s1 = "Hello"; String s2 = "Hello"; String s3 = "Hel" + "lo"; String s4 = "Hel" + new String("lo"); String s5 = new String("Hello"); String s6 = s5.intern(); String s7 = "H"; String s8 = "ello"; String s9 = s7 + s8; System.out.println(s1 == s2); // true System.out.println(s1 == s3); // true System.out.println(s1 == s4); // false System.out.println(s1 == s9); // false System.out.println(s4 == s5); // false System.out.println(s1 == s6); // true
Zuallererst wird in Java der ==-Operator direkt verwendet, um die Referenzadressen zweier Zeichenfolgen zu vergleichen, nicht den Inhalt. Bitte verwenden Sie String.equals(), um den Inhalt zu vergleichen.
s1 == s2 ist sehr einfach zu verstehen. Wenn Sie s1 und s2 Werte zuweisen, bedeutet dies, dass die Zeichenfolge direkt zu Tode geschrieben wird Die Anzahl der Literale wird direkt in den Konstantenpool der Klassendatei eingefügt, um eine Wiederverwendung zu erreichen. Nach dem Laden des Laufzeitkonstantenpools verweisen s1 und s2 auf dieselbe Speicheradresse, sodass sie gleich sind.
s1 == s3 hat eine Gefahr. Obwohl s3 eine dynamisch gespleißte Zeichenfolge ist, sind alle am Spleißen beteiligten Teile bekannte Literale, die vom Compiler direkt buchstabiert werden Für Sie wird String s3 = „Hel“ + „lo“; in der Klassendatei zu String s3 = „Hello“ optimiert, sodass s1 == s3 erstellt wird.
Natürlich ist s1 == s4 nicht gleich, aber der neue String("lo") ist kein bekanntes Literal und wird vom Compiler nicht optimiert Das Ergebnis kann erst zur Laufzeit bestimmt werden. In Kombination mit dem String-Invarianz-Theorem weiß wer, wo s4 zugeordnet ist. Daher muss die Adresse unterschiedlich sein. Zur Verdeutlichung der Idee wird ein einfaches Diagramm bereitgestellt:
s1 == s9 ist nicht gleich, und der Grund ist ähnlich. Obwohl s7 und s8 beim Zuweisen von Werten Zeichenfolgenliterale verwenden, wenn sie gespleißt werden in s9Da zwei Variablen, s7 und s8, unvorhersehbar sind, ist der Compiler ein Compiler und kann nicht als Interpreter verwendet werden. Wenn er ausgeführt wird, wird die neue Zeichenfolge von s7 und s8 gespleißt befindet sich an der Adresse im Heap. Ich bin mir nicht sicher, es kann nicht mit der S1-Adresse im Konstantenpool im Methodenbereich identisch sein.
s4 == s5 bedarf keiner Erklärung mehr, absolut nicht gleich, beide liegen im Heap, haben aber unterschiedliche Adressen.
s1 == s6 Diese beiden Gleichheiten sind vollständig darauf zurückzuführen, dass sich s5 im Heap befindet und der Inhalt „Hello“ ist. Die interne Methode versucht, die Zeichenfolge „Hello“ zum Konstantenpool hinzuzufügen und zurückzugeben Da der Konstantenpool bereits einen Hello-String enthält, gibt die interne Methode die Adresse direkt zurück und s1 zeigt bereits während der Kompilierung auf den Konstantenpool, sodass s1 und s6 auf dieselbe Adresse verweisen und gleich sind.
An dieser Stelle können wir drei sehr wichtige Schlussfolgerungen ziehen:
Wir müssen auf das Verhalten zur Kompilierungszeit achten, um den konstanten Pool besser zu verstehen.
Die Konstanten im Laufzeitkonstantenpool stammen grundsätzlich aus dem Konstantenpool in jeder Klassendatei.
Wenn das Programm ausgeführt wird, fügt der JVM nicht automatisch Konstanten zum Konstantenpool hinzu, es sei denn, Sie fügen manuell Konstanten zum Konstantenpool hinzu (z. B. durch Aufrufen der Intern-Methode).
Das Obige betrifft nur String-Konstantenpools. Tatsächlich gibt es auch Integer-Konstantenpools, Gleitkomma-Konstantenpools usw., aber sie sind alle ähnlich, außer dass Sie Konstanten vom numerischen Typ nicht manuell hinzufügen können Die Konstanten im Konstantenpool wurden beim Programmstart festgelegt. Der Konstantenbereich im Ganzzahlkonstantenpool ist beispielsweise: -128~127. Im Konstantenpool können nur Zahlen in diesem Bereich verwendet werden.
Praxis
Nachdem wir so viel Theorie gesagt haben, kommen wir nun zum eigentlichen konstanten Pool.
Wie bereits erwähnt, gibt es einen statischen Konstantenpool in der Klassendatei. Dieser Konstantenpool wird vom Compiler generiert und zum Speichern von Literalen in Java-Quelldateien verwendet (angenommen, wir). Es gibt den folgenden Java-Code:
String s = "hi";
为了方便起见,就这么简单,没错!将代码编译成class文件后,用winhex打开二进制格式的class文件。如图:
简单讲解一下class文件的结构,开头的4个字节是class文件魔数,用来标识这是一个class文件,说白话点就是文件头,既:CA FE BA BE。
紧接着4个字节是java的版本号,这里的版本号是34,因为笔者是用jdk8编译的,版本号的高低和jdk版本的高低相对应,高版本可以兼容低版本,但低版本无法执行高版本。所以,如果哪天读者想知道别人的class文件是用什么jdk版本编译的,就可以看这4个字节。
接下来就是常量池入口,入口处用2个字节标识常量池常量数量,本例中数值为00 1A,翻译成十进制是26,也就是有25个常量,其中第0个常量是特殊值,所以只有25个常量。
常量池中存放了各种类型的常量,他们都有自己的类型,并且都有自己的存储规范,本文只关注字符串常量,字符串常量以01开头(1个字节),接着用2个字节记录字符串长度,然后就是字符串实际内容。本例中为:01 00 02 68 69。
接下来再说说运行时常量池,由于运行时常量池在方法区中,我们可以通过jvm参数:-XX:PermSize、-XX:MaxPermSize来设置方法区大小,从而间接限制常量池大小。
假设jvm启动参数为:-XX:PermSize=2M -XX:MaxPermSize=2M,然后运行如下代码:
//保持引用,防止自动垃圾回收 List<String> list = new ArrayList<String>(); int i = 0; while(true){ //通过intern方法向常量池中手动添加常量 list.add(String.valueOf(i++).intern()); }
程序立刻会抛出:Exception in thread "main" java.lang.outOfMemoryError: PermGen space异常。PermGen space正是方法区,足以说明常量池在方法区中。
在jdk8中,移除了方法区,转而用Metaspace区域替代,所以我们需要使用新的jvm参数:-XX:MaxMetaspaceSize=2M,依然运行如上代码,抛出:java.lang.OutOfMemoryError: Metaspace异常。同理说明运行时常量池是划分在Metaspace区域中。具体关于Metaspace区域的知识,请读者自行搜索。
更多java知识请关注java基础教程栏目。
Das obige ist der detaillierte Inhalt vonDetaillierte Grafik- und Texterklärung des Java-Konstantenpools. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!