定数プールエリアでのJavaメソッド

はじめにテキストグローバル文字列プール(文字列プールも文字列リテラルと呼ばれるプール)クラスファイルの定数プール(クラスの定数プール)ランタイム定数プール(ランタイム定数プール)3つの定数プールとの間の関係の概要参照リンク

序文

また、静的な記憶領域として知られているヒープメモリ(ヒープ)、スタック・メモリ(スタック)およびメソッド領域(方法):JavaのJVMメモリは、3つのゾーンに分割することができます。

データ上のセクションでは、==の比較を行いながら、学習の過程では、多くの場合、長期的な定数プールを聞くだろう、というの問い合わせ時に、文字列定数プールに言及定数プールもないヒープ、スタックやメモリ 、そして、定数プールは、分類のための定数プールにも一定の理解を持っている間、一定のプールおよび方法に関連する分野を理解するために、「素人JVM」の本を読んでいる関係、およびメソッド地区、かもしれません。

この記事は、すべての行動規範のJDK1.8に基づいています。

テキスト

一定であるかを理解するには、定数プールの必要性の種類を議論する前に。

  • 与えられた後、最終的な改変体の変数が一定値を表すと変更することはできません!
  • それぞれ静的変数、ローカル変数とインスタンス変数、定数の三種類、三つの変数最後の変更があります。

Javaのメモリの割り当てで、定数プールの三種類の合計:

グローバル文字列プール(文字列プールは、文字列リテラルプールと呼ばれています)

Javaのメモリ領域内の文字列定数プール

  • JDK6.0と以前に、文字列定数プールは、オブジェクトが定数プールに格納されている場合にはパーマ世代領域(即ち、領域法)です。
  • JDK7.0のバージョンでは、文字列定数プールは、ヒープに移動されます。このとき、一定のストレージ・プールが参照されています。JDK8.0では、永久世代(面積法)元素空間で置換されています。

文字列定数プールとは何ですか?

在 HotSpot VM 里实现的 string pool 功能的是一个 StringTable 类,它是一个 Hash 表,默认值大小长度是1009;里面存的是驻留字符串的引用(而不是驻留字符串实例自身)。也就是说某些普通的字符串实例被这个 StringTable 引用之后就等同被赋予了“驻留字符串”的身份。这个 StringTable 在每个 HotSpot VM 的实例里只有一份,被所有的类共享。

StringTable 本质上就是个 HashSet<String>。这是个纯运行时的结构,而且是惰性(lazy)维护的。注意它只存储对java.lang.String 实例的引用,而不存储 String 对象的内容。 注意,它只存了引用,根据这个引用可以得到具体的 String 对象。

在 JDK6.0 中,StringTable 的长度是固定的,长度就是 1009,因此如果放入 String Pool 中的 String 非常多,就会造成 hash 冲突,导致链表过长,当调用 String#intern() 时会需要到链表上一个一个找,从而导致性能大幅度下降;

在 JDK7.0 中,StringTable 的长度可以通过参数指定:

-XX:StringTableSize=66666
复制代码

class 文件常量池(class constant pool)

我们都知道,class 文件中除了包含类的版本、字段、方法、接口等描述信息外,还有一项信息就是常量池(constant pool table),用于存放编译器生成的各种字面量(Literal)和符号引用(Symbolic References)字面量比较接近 Java 语言层面常量的概念,如文本字符串、被声明为 final 的常量值等。 符号引用则属于编译原理方面的概念,包括了如下三种类型的常量:

  • 类和接口的全限定名
  • 字段的名称和描述符
  • 方法的名称和描述符

常量池的每一项常量都是一个表,一共有如下表所示的11种各不相同的表结构数据,这每个表开始的第一位都是一个字节的标志位(取值1-12),代表当前这个常量属于哪种常量类型。


一定の種類のそれぞれ異なるタイプは、彼らがここに記載されないであろう、異なる構造、コンクリート構造を持って、この記事では、3つの概念的な区別定数プールに焦点を当てて(データ構造の各タイプについて詳しく学ぶために一定の読者が見ることができる「深い理解第VI章のJava仮想マシン」、 実際に、私はまだ、フォローバック満たされたピットを理解していません )。

ランタイム定数プール(ランタイム定数プール)

実行時定数プールは、ゾーン方式の一部です。

ファイルはつまり、Javaクラスファイルにコンパイルされたとき、それは、上記のクラスの定数プールの上に生成され、それを生産する際には、実行時定数プールはありますか?

クラスの実装ではJVMは、通過しなければならない加载、连接、初始化、との接続は、三の段階で、検証、製造、解決(解決)を含みます。メモリにロードされたときにクラスの後、JVMは、そのクラスファイルの定数プールをもたらし、実行時定数プールに格納されて見ることができ、すべてのクラスの実行時定数プールは、いずれかを持っています。クラスの定数プールが保持され、上記の前記字面量和符号引用格納されたオブジェクト・インスタンスが、シンボルオブジェクト参照値ではないこと、。そして、シンボルである決意、後に直接引用への参照を置き換え、クエリの解析プロセスは、グローバル文字列文字列プールのランタイム定数プールが引用していることを確認します。RESDLL.DLL上記されているグローバル文字列プールを、行きます引用一貫しています。

3つの一定のプール間の関連

時間JVMの実行について、だけでなく、へ字符串常量池

在类加载阶段, JVM 会在堆中创建对应这些 class 文件常量池中的字符串对象实例,并在字符串常量池中驻留其引用。具体在 resolve 阶段执行。这些常量全局共享。
复制代码

ここではより一般的な、はい、解決舞台ことが、私たちは考えて、すぐに文字列定数プールの参考文献にその存在するオブジェクトを作成していないよう。JVM仕様では、明示的に位相が遅延することができ解決しました。

JVM 规范里 Class 文件常量池项的类型,有两种东西:CONSTANT_Utf8 和CONSTANT_String。前者是 UTF-8 编码的字符串类型,后者是 String 常量的类型,但它并不直接持有 String 常量的内容,而是只持有一个 index,这个 index 所指定的另一个常量池项必须是一个 CONSTANT_Utf8 类型的常量,这里才真正持有字符串的内容。

在HotSpot VM中,运行时常量池里,

CONSTANT_Utf8 -> Symbol*(一个指针,指向一个Symbol类型的C++对象,内容是跟Class文件同样格式的UTF-8编码的字符串)
CONSTANT_String -> java.lang.String(一个实际的Java对象的引用,C++类型是oop)
复制代码

CONSTANT_Utf8 会在类加载的过程中就全部创建出来,而 CONSTANT_String 则是 lazy resolve 的,例如说在第一次引用该项的 ldc 指令被第一次执行到的时候才会 resolve。那么在尚未 resolve 的时候,HotSpot VM 把它的类型叫做JVM_CONSTANT_UnresolvedString,内容跟 Class 文件里一样只是一个 index;等到 resolve 过后这个项的常量类型就会变成最终的 JVM_CONSTANT_String,而内容则变成实际的那个 oop。

看到这里想必也就明白了, 就 HotSpot VM 的实现来说,加载类的时候,那些字符串字面量会进入到当前类的运行时常量池,不会进入全局的字符串常量池(即在 StringTable 中并没有相应的引用,在堆中也没有对应的对象产生)。所以上面提到的,经过 resolve 时,会去查询全局字符串池,最后把符号引用替换为直接引用。(即字面量和符号引用虽然在类加载的时候就存入到运行时常量池,但是对于 lazy resolve 的字面量,具体操作还是会在 resolve 之后进行的。)

关于 lazy resolution 需要在这里了解一下 ldc 指令

简单地说,它用于将 String 型常量值从常量池中推送至栈顶。

以下面代码为例:

    public static void main(String[] args) {
        String s = "abc";
    }
复制代码

例えば、コードファイルはまず、ディレクトリDOSウィンドウでファイルを開いて実行し、Test.javaでjavac Test.javaコンパイルをし、enter javap -verbose Test、次のようにViewクラスファイルがコンパイルされています。


使用LDC命令「ABC」はオペランドスタックにロードされ、その後、astore_1に割り当てている私たちはローカル変数を定義し、次に戻りますよ。

解決フェーズ(定数プール解像度)、作成される文字列リテラルオブジェクトとその参照ストリング定数プールの存在で、たとえば上に結合、これは怠惰な解決です。つまり、本当のオブジェクトが存在しない、文字列定数は、プールは自然に、そしてどのようにLDC命令は、スタックの値と割り当ての先頭にプッシュされませんか?または解決ステージは本当に正しいそれを実行するための時間が常に存在すること、怠け者であるため、異なる角度をしたい、何時間?

LDC命令の実行条件は、怠惰な解像度のアクションをトリガーされます

ここで実行セマンティクスバイトコードLDCは、次のとおりです。現在実行中のクラス定数は、アイテムがまだ決意を解決していない場合、インデックスに対応するエントリを見つけるために、(実行時定数プール、HotSpot VMのはConstantPool + ConstantPoolCacheである)プール、決意の内容を返します。
型文字列定数の面では、それが見つかった場合ますRESDLL.DLLコンテンツのjava.lang.String基準に合致しているプロセスを解決するには、この基準が直接戻され、逆に、コンテンツはまだ次に、文字列のインスタンスを参照します。RESDLL.DLLと一致していない場合Javaヒープに、コンテンツに対応する文字列オブジェクトを作成します。RESDLL.DLLレコード下、この参照、参照を返します。

可視光は、LDC命令は、それはすべてに依存し、新しい文字列インスタンスを作成するために必要とされる場合、この1つのLDC命令の最初の実行、ますRESDLL.DLLレコードへの参照は、文字列の内容に対応するかどうか。

次のコードに示すと分析を行います。

 public static void main(String[] args) {
           String s1 = "abc";  
        String s2 = "abc";
        String s3 = "xxx";
    }
复制代码

次のようにクラスファイルは、コンパイルされ表示します:

在这里插入图片描述
ここに画像を挿入説明

グラフィカルな方法を示しています。
在这里插入图片描述
ここに画像を挿入説明

String s1 = "abc"; 文字列定数プール内の解決法は、「ABC」への参照、彼らは、新しい「ABC」オブジェクトを作成し、ヒープやプールに文字列定数へのオブジェクト参照、S1にこのリファレンスバックは認められませんでした。

String s2 = "abc"; 解決プロセスは、ますRESDLL.DLLは、「ABC」オブジェクトが参照S2に直接返され、任意のオブジェクトを作成しないで引用されているでしょう。

String s3 = "xxx"; コードの最初の行のように、ヒープ・オブジェクトを作成し、そしてますRESDLL.DLLに格納されたオブジェクト参照は、最終的にはS3への参照を返します。

インターン定数プールと方法

 public static void main(String[] args) {
           String s1 = "ab";//#1
        String s2 = new String(s1+"d");//#2
        s2.intern();//#3
        String s4 = "xxx";//#4
        String s3 = "abd";//#5
        System.out.println(s2 == s3);//true
    }
复制代码

次のようにクラスファイルは、コンパイルされ表示します:

クラスファイルの情報によりクラスは、ステージの怠惰な決意であるので、それはないドウェルに、オブジェクトのインスタンスを作成しません、クラスファイルの定数プールにいる、「AB」、「D」、「XXX」、「ABD」を示します文字列定数プール。

次のように示します:


コードの解釈の各ラインのための主要な方法、に。

  • 図1は、LDC命令は、「AB」はヒープに保存された文字列定数プールに「AB」オブジェクト及び参照オブジェクトを作成するために、換言すれば、スタックの最上位にロードされるであろう。
  • 2は、LDC命令は、「d」はインテリアは、すべての方法APPENDに、最後の呼び出しStringオブジェクトを取得するには(内容はABDで、ノートのtoStringメソッドののtoStringメソッドStringBuilderオブジェクトをStringBuilderオブジェクトを作成され、その後、スプライシング操作があり、スタックの一番上にロードされます)新しい文字列オブジェクトであり、及び(割り当てのみS2参照の対象のまま)S2に割り当てます。注意此时没有把“abd”对象的引用放入字符串常量池。
  • 3、インターン方法最初の文字列定数プールがある場合、「ABD」オブジェクトへの参照を見つけると、そうでない場合にするには、文字列定数プールへのオブジェクト参照の保存杭「ABD」を入れて、参照を返しますが、我々私はそれを受け取る変数を使用していませんでした。
  • 図4に示すように、無意味は、例示の目的でのみ、「ABD」リテラルクラスファイル#5が得られます。
  • 5,字符串常量池中已经有“abd”对象的引用,因此直接将该引用返回给 s3。

总结

1、全局字符串常量池在每个 VM 中只有一份,存放的是字符串常量的引用值。

2、class 常量池是在编译的时候每个 class 都有的,在编译阶段,存放各种字面量和符号引用。

3、运行时常量池是在类加载完成之后,将每个class常量池中的符号引用值转存到运行时常量池中,也就是说,每个 class 都有一个运行时常量池,类在解析之后,将符号引用替换成直接引用,与全局常量池中的引用值保持一致。

4、class 文件常量池中的字符串字面量在类加载时进入到运行时常量池,在真正在 resolve 阶段(即执行 ldc 指令时)时将该字符串的引用存入到字符串常量池中,另外运行时常量池相对于 class 文件常量池具备动态性,有些常量不一定在编译期产生,也就是并非预置入 class 文件常量池的内容才能进入到方法区运行时常量池,运行期间通过 intern 方法,将字符串常量存入到字符串常量池中和运行时常量池(关于优先进入到哪个常量池,私以为先进入到字符串常量池,具体实现还望大神指教)。

参考链接

おすすめ

転載: juejin.im/post/5dc2ce826fb9a04ab12bc4e6