Java 定数プールは永遠のトピックであり、面接官のお気に入りです。さまざまな質問があります。Xiaocai は定数プールについてすでに聞いているので、今回はそれをうまくまとめます。
推奨: java ビデオ チュートリアル
jvm 仮想メモリの配布:
プログラム カウンターは、 jvm でプログラムを実行する パイプラインにはいくつかのジャンプ命令が格納されていますが、これは高度すぎて Xiao Cai には理解できません。
ローカル メソッド スタックは、jvm がオペレーティング システムのメソッドを呼び出すために使用するスタックです。
仮想マシン スタックは、Java コードを実行するために jvm によって使用されるスタックです。
メソッド領域には、いくつかの定数、静的変数、クラス情報などが格納されます。これは、メモリ内のクラス ファイルの格納場所として理解できます。
仮想マシン ヒープは、Java コードを実行するために jvm によって使用されるヒープです。
Java の定数プールは、実際には、静的定数プールと実行時定数プールの 2 つの形式に分かれています。
いわゆる静的定数プールは、*.class ファイル内の定数プールです。クラス ファイル内の定数プールには、文字列 (数値) リテラルだけでなく、クラスとメソッドの情報も含まれており、絶対に占有されます。クラスファイルの一部ではなく、スペースの大部分。
ランタイム定数プールとは、jvm 仮想マシンがクラスのロード操作を完了した後、クラス ファイル内の定数プールをメモリにロードし、メソッド領域に保存することを意味します。これを定数プールと呼ぶことがよくあります。 . は、メソッド領域のランタイム定数プールを指します。
次に、インターネット上でよく知られている定数プールの例をいくつか引用して説明します。
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
まず、Java で == 演算子を直接使用する場合、比較されるのは 2 つの文字列の内容ではなく、参照アドレスであることを説明します。内容を比較します。
s1 == s2 は非常に分かりやすいです。s1 と s2 に値を代入するとき、どちらも文字列リテラルを使用します。平たく言えば、文字列を直接書き出すことを意味します。コンパイル時に、この種のリテラルは、再利用を実現するためにクラス ファイルの定数プールに直接配置されます。ランタイム定数プールをロードした後、s1 と s2 は同じメモリ アドレスを指すため、それらは等しくなります。
s1 == s3 には落とし穴があります。s3 は動的に結合された文字列ですが、結合に関係するすべての部分は既知のリテラルです。コンパイル中に、この結合は最適化されます。コンパイラはそれを直接綴ります。つまり、クラスファイルでは String s3 = "Hel" "lo"; が String s3 = "Hello"; に最適化され、s1 == s3 が成立します。
s1 == s4 は確かに等しくありません。s4 も結合されていますが、新しい String("lo") 部分は既知のリテラルではなく、予測不可能な部分です。コンパイラはそれを最適化しません。結果文字列不変定理と組み合わせると、s4 がどこに割り当てられるかは誰にも分からないため、アドレスは異なるはずです。アイデアを明確にするために簡単な図を示します:
s1 == s9 は等しくありません、そして理由は同様です。s7 と s8 は値を割り当てるときに文字列リテラルを使用しますが、それらが結合されるときはs9 に, 2 つの変数として, s7 と s8 は予測できません. 結局のところ, コンパイラはコンパイラであり, インタプリタとして使用できないため, 最適化は行われません. 実行されると, 新しい文字列は s7 と s8 によって結合されますヒープ内のアドレスになりますが、メソッド領域の定数プール内の s1 アドレスと同じにすることはできません。
s4 == s5 は説明の必要はありません。絶対に等しくありません。両方ともヒープ内にありますが、アドレスが異なります。
s1 == s6 これら 2 つの等価性は、完全にインターン メソッドによるものです。s5 はヒープ内にあり、コンテンツは Hello です。インターン メソッドは、Hello 文字列を定数プールに追加して、それを返そうとします。定数プールにはすでに Hello 文字列が存在するため、インターン メソッドはアドレスを直接返し、s1 はコンパイル中にすでに定数プールを指しているため、s1 と s6 は同じアドレスを指しており、等しくなります。
この時点で、非常に重要な 3 つの結論を導き出すことができます。
定数プールをよりよく理解するには、コンパイル時の動作に注意を払う必要があります。
ランタイム定数プールの定数は、基本的に各クラス ファイルの定数プールから取得されます。
プログラムの実行中、定数が定数プールに手動で追加されない限り (インターン メソッドの呼び出しなど)、jvm は定数を定数プールに自動的に追加しません。
上記には文字列定数プールのみが関係します。実際には、整数定数プール、浮動小数点定数プールなどもありますが、それらはすべて似ています。ただし、数値定数プールに手動で定数を追加することはできません。タイプ定数プールです。定数プールの定数はプログラムの起動時に決定されます。たとえば、整数定数プールの定数範囲は -128 ~ 127 です。定数プールではこの範囲の数値のみを使用できます。
実践
理論についてはここまで述べたので、実際の定数プールについて触れてみましょう。
前に述べたように、クラス ファイルには静的定数プールがあります。この定数プールはコンパイラによって生成され、Java ソース ファイルにリテラルを格納するために使用されます (この記事ではリテラルのみに焦点を当てます)。次の Java コードがあります:
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基础教程栏目。
以上がJava定数プールの詳細なグラフィックとテキストの説明の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。