JAVA三種類の定数池の違い

4954 ワード

1.グローバル文字列定数プール(string poolはstring literal poolとも呼ばれる)
グローバル文字列プールの内容は、クラスのロードが完了し、検証され、準備フェーズの後、スタック内で文字列オブジェクトインスタンスを生成し、その文字列オブジェクトインスタンスの参照値をstring poolに保存します(string poolには、特定のインスタンスオブジェクトではなく参照値が保存され、特定のインスタンスオブジェクトはスタック内で開かれた空間に保存されていることを覚えておいてください).HotSpot VMで実装されるstring pool機能はStringTableクラスで、key(字面量「abc」、すなわち、常駐文字列)-value(文字列「abc」インスタンスオブジェクトのスタックでの参照)キー値ペアが格納されています.すなわち、スタック内のいくつかの文字列インスタンスがこのStringTableによって参照されると、「常駐文字列」のアイデンティティが付与されることになる.このStringTableは各HotSpot VMのインスタンスに1部しかなく、すべてのクラスで共有されている(メタモード)
文字列定数プールは、JVMメモリのメソッド領域に保存されます.
2.classファイル定数プール(class constant pool)
クラスファイルには、クラスのバージョン、フィールド、メソッド、インタフェースなどの記述情報のほかに、コンパイラが生成した様々な字面量(Literal)とシンボルリファレンス(Symbolic References)を格納するための定数プール(constant pool table)が含まれていることが知られています.字面量は,テキスト文字列,finalとして宣言された定数値など,我々が言う定数概念である.シンボル参照は、参照されたターゲットを記述するためのシンボルのセットであり、シンボルは任意の形式の字面量であってもよく、使用時にターゲットに曖昧に位置決めできる限り(直接参照と区別して、直接参照は一般的にメソッド領域を指すローカルポインタ、相対オフセット量、またはターゲットに間接的に位置決めできるハンドルである).一般的には、次の3つの定数が含まれます.
  • クラスとインタフェースのフルリミット名
  • フィールドの名前と記述子
  • メソッドの名前と記述子
  • 定数プールの各定数は1つのテーブルで、次の表に示す11種類の異なるテーブル構造データがあります.各テーブルの最初のビットは、現在の定数がどの定数タイプに属しているかを表す1バイトのフラグビットです.各タイプの定数タイプには異なる構造があり、具体的な構造は本稿では先に述べないが、この3つの定数プールの概念を区別することに重点を置いている(読者が各定数タイプのデータ構造を深く理解するには、「java仮想マシンを深く理解する」第6章の内容を参照することができる).
    これはClassファイルの内容で、まだ実行時の内容ではありません.プールであることを理解しないでください.実はClassファイルのバイトコード命令です.
    3.運転時定数プール(runtime constant pool)
    Javaファイルがclassファイルにコンパイルされると、つまり私が上述したclass定数プールが生成されますが、実行時定数プールはいつ生成されますか?
    jvmはクラスをロードするときに、マウント、接続、初期化を経なければならないが、接続には検証、準備、解析の3つの段階が含まれている.クラスがメモリにロードされると、jvmはclass定数プールの内容を実行時定数プールに格納します.これにより、実行時定数プールも各クラスに1つあることがわかります.前述したように、class定数プールに格納されているのは、字面量とシンボル参照です.つまり、オブジェクトのインスタンスではなく、オブジェクトのシンボル参照値です.解析(resolve)後、すなわちシンボル参照を直接参照に置き換えると、解析の過程でグローバル文字列プール、すなわちStringTableがクエリされ、実行時定数プールで参照される文字列がグローバル文字列プールで使用される文字列と一致することを保証します.
    実行時定数プールは、JVMメモリのメソッド領域に保存されます.
    例1:
    int a = 1;
    String b = "asd";

    1
    2
  • まず、1および"asd"はjavac(または他のコンパイラ)コンパイル後にClassファイル中のconstant_pool tableのコンテンツ
  • になる.
  • 私たちのプログラムが実行されるとき、つまりJVMが実行されるとき、各Class constant_pool tableの内容はJVMメモリのメソッド領域のそれぞれのClassのRuntime Constant Pool
  • にロードされます.
  • String Poolに含まれていないRuntime Constant Poolの文字列(ここでは"asd")がString Poolに追加されます(HosSpotはhashtable参照方式を使用します).
  • Java Heapにおいて"asd"字面量createの文字列オブジェクト
  • に従う.
  • 字面量"asd"と文字列オブジェクトの参照をhashtableに関連付け、キー値形式は:"asd"=
  • である.

    例2:
    1 2 3 4 5 6 7 8
    String str1 = "abc"; String str2 = new String("def"); String str3 = "abc"; String str4 = str2.intern(); String str5 = "def"; System.out.println(str1 == str3);//true System.out.println(str2 == str4);//false System.out.println(str4 == str5);//true

    上記のプログラムは、まずコンパイルされた後、クラスのclass定数プールにいくつかのシンボル参照を格納し、クラスがロードされた後、class定数プールに格納されたシンボル参照を実行時定数プールに転送し、検証を経て、準備フェーズの後、スタックに常駐文字列を生成するインスタンスオブジェクト(すなわち、前例のstr 1が指す「abc」インスタンスオブジェクト)を生成し、次に、このオブジェクトの参照をグローバルString Pool、すなわちStringTableに保存し、最後に解析フェーズで、実行時定数プールのシンボル参照を直接参照に置き換えるには、StringTableを直接クエリーし、StringTableの参照値が実行時定数プールの参照値と一致することを保証します.
    上のプログラムに戻ると、プログラム全体のメモリ割り当てプロセスが簡単に説明できます.まず、スタックに「abc」インスタンスがあり、グローバルStringTableに「abc」の参照値が格納され、2番目の文を実行するときに「def」のインスタンスオブジェクトが2つ生成され、StringTableに「def」の参照値が格納されます.もう1つはnewから出てきた「def」のインスタンスオブジェクトで、前述とは異なるインスタンスであり、str 3を解析するときにStringTableを検索し、「abc」のグローバル駐在文字列参照が含まれているため、str 3の参照アドレスは以前の既存のものと同じであり、str 4は実行時にintern()関数を呼び出し、StringTableの「def」の参照値を返す.str 2の参照値を追加しなかった場合、ここでStringTableには既に「def」の参照値があるので、new str 2のときにStringTableに追加した「def」の参照値を返し、最後にstr 5は解析時にもStringTableに存在する「def」の参照値を指すので、このような解析の後、次の3つの印刷の値は分かりやすいです.
    まとめ
  • 1.グローバル定数プールはVMごとに1つしかなく、key(字面量「abc」)-value(文字列「abc」インスタンスオブジェクトのスタック内の参照)キー値ペアが格納されています.
  • 2.class定数プールはclassバイトコードファイルの内容であり、コンパイル段階ではクラスに関連する定数が格納される.
  • 3.ランタイムプールはランタイムの内容であり、定数プールから変換されたものである.各class定数プールのシンボル参照値を実行時定数プールに転送します.つまり、各classには実行時定数プールがあり、クラスは解析後、シンボル参照を直接参照に置き換え、グローバル定数プールの参照値と一致します.