定数プール実行時定数プールString internメソッドから考える(一)


最近『Java仮想マシンを深く理解する』を見て、コンスタントプール(Constant Pool Table)とランタイムプール(Runtime Constant Pool)の2つの概念を見て、この2つの概念についてよく理解していません.Stringクラスのinternメソッドも見られましたが、internメソッドも使ったことがありません.そこで、調べてみると、以下のように記録されています.後で問題にぶつかった後、blogを書くときは、まず問題の結論を下します.
結論
定数プールという概念はjava classファイルについてです.Javaコードがバイトコード(class)ファイルにコンパイルされると、文字列量(finalの定数値として宣言されたテキスト文字列)、シンボル参照がclassファイルの定数プールに格納されます.実行時定数プールは、JVMメモリのメソッド領域の一部です.classファイルがJVMにロードされると、定数プールの内容は、perm領域、すなわち永続世代の実行時定数プールに格納されます.上記の文はJDK 1.6および以前のバージョンにのみ適用されます.JDK 1.7の後に方法領域をいくつか最適化した.1.7以降、実行時定数プールをスタックに移動します.そのため、Perm領域はクラスの静的な領域であり、主にいくつかのロードクラスの情報、定数プール、メソッドフラグメントなどの内容が格納され、デフォルトのサイズは4 Mしかありません.範囲を超えるとjava.lang.OutOfMemoryError:PermGen spaceエラーが直接発生します.internメソッド:メソッドを呼び出すと、ランタイムプールで文字列の有無が表示され、ある場合はリファレンス(ランタイムプールのアドレス)が返されます.ない場合は、ランタイムプールにデータを格納し、ランタイムプールの文字列のアドレスを返します.これはJDK 1.6以前に起こったことです.1.7以降であれば、ランタイムプールにないことが判明した場合、ランタイムプールにはこの文字列Stringの参照(つまり、このStringオブジェクトのスタック内のアドレス)が格納され、その参照の値が返されます.
究極の結論
究極の結論はJDK 1.7以降である.定数プールはclassファイルの字面量およびシンボル参照(シンボル参照とはjavaコンパイラによってコンパイルされた後に現れる様々なクラスを指す[javaコンパイラがコンパイルされた後に現れる様々なクラスであり、javaソースコードに現れるクラスではないのは、javaコンパイラがjavaソースコードをStringの"+"操作などの最適化するためである]および使用するメソッド名、パラメータ、戻り値).ランタイムプールとは、classファイルがJVMにロードされた後、定数プールに格納されたメモリ領域であり、このメモリ領域はHeap領域に属する.interメソッド:stringオブジェクトsがinternを呼び出すと(s.intern())、ランタイムプールにsの値がある場合は、その文字列がランタイムプールにあるアドレスを直接返します.sの値がない場合は、ランタイムプールにsのアドレス(参照)を格納し、sのアドレス(参照)を返します.次の「sの値がない場合は、sのアドレス(参照)ランタイム・ボリューム・プールに格納され、sのアドレス(参照)が返されます.次に例を示します.
public class Test {
    public static void main(String[] args) {
        String s = new String("12") + new String("3");
        s.intern();
    }
}
この2つの言葉はmainメソッドが入ってから実行されます.
String s = new String("12") + new String("3");
この文はコンパイラによって最適化されます(バイトコード命令からわかります).上のコードに対応するバイトコード命令は、次のコマンドで表示できます.
//  java  
java -version
javac Test.java     //  
//-verbose  class       
//-c      JVM     
javap -verbose -c Test  
この文書で使用するjavaバージョンは次のとおりです.
java version "1.6.0_45"
Java(TM) SE Runtime Environment (build 1.6.0_45-b06)
Java HotSpot(TM) 64-Bit Server VM (build 20.45-b01, mixed mode)
上のコマンドの完全な出力は次のとおりです.
Compiled from "Test.java"
public class Test extends java.lang.Object
  SourceFile: "Test.java"
  minor version: 0
  major version: 50
  Constant pool:
const #1 = Method #12.#21; // java/lang/Object."<init>":()V
const #2 = class #22; // java/lang/StringBuilder
const #3 = Method #2.#21; // java/lang/StringBuilder."<init>":()V
const #4 = class #23; // java/lang/String
const #5 = String #24; // 12
const #6 = Method #4.#25; // java/lang/String."<init>":(Ljava/lang/String;)V
const #7 = Method #2.#26; // java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
const #8 = String #27; // 3
const #9 = Method #2.#28; // java/lang/StringBuilder.toString:()Ljava/lang/String;
const #10 = Method #4.#29; // java/lang/String.intern:()Ljava/lang/String;
const #11 = class #30; // Test
const #12 = class #31; // java/lang/Object
const #13 = Asciz <init>;
const #14 = Asciz ()V;
const #15 = Asciz Code;
const #16 = Asciz LineNumberTable;
const #17 = Asciz main;
const #18 = Asciz ([Ljava/lang/String;)V;
const #19 = Asciz SourceFile;
const #20 = Asciz Test.java;
const #21 = NameAndType #13:#14;// "<init>":()V
const #22 = Asciz java/lang/StringBuilder;
const #23 = Asciz java/lang/String;
const #24 = Asciz 12;
const #25 = NameAndType #13:#32;// "<init>":(Ljava/lang/String;)V
const #26 = NameAndType #33:#34;// append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
const #27 = Asciz 3;
const #28 = NameAndType #35:#36;// toString:()Ljava/lang/String;
const #29 = NameAndType #37:#36;// intern:()Ljava/lang/String;
const #30 = Asciz Test;
const #31 = Asciz java/lang/Object;
const #32 = Asciz (Ljava/lang/String;)V;
const #33 = Asciz append;
const #34 = Asciz (Ljava/lang/String;)Ljava/lang/StringBuilder;;
const #35 = Asciz toString;
const #36 = Asciz ()Ljava/lang/String;;
const #37 = Asciz intern;

{
public Test();
  Code:
   Stack=1, Locals=1, Args_size=1
   0:   aload_0
   1:   invokespecial   #1; //Method java/lang/Object."<init>":()V
   4:   return
  LineNumberTable: 
   line 1: 0


public static void main(java.lang.String[]);
  Code:
   Stack=4, Locals=2, Args_size=1
   0:   new     #2; //class java/lang/StringBuilder
   3:   dup
   4:   invokespecial   #3; //Method java/lang/StringBuilder."<init>":()V
   7:   new     #4; //class java/lang/String
   10:  dup
   11:  ldc     #5; //String 12
   13:  invokespecial   #6; //Method java/lang/String."<init>":(Ljava/lang/String;)V
   16:  invokevirtual   #7; //Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
   19:  new     #4; //class java/lang/String
   22:  dup
   23:  ldc     #8; //String 3
   25:  invokespecial   #6; //Method java/lang/String."<init>":(Ljava/lang/String;)V
   28:  invokevirtual   #7; //Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
   31:  invokevirtual   #9; //Method java/lang/StringBuilder.toString:()Ljava/lang/String;
   34:  astore_1
   35:  aload_1
   36:  invokevirtual   #10; //Method java/lang/String.intern:()Ljava/lang/String;
   39:  pop
   40:  return
  LineNumberTable: 
   line 3: 0
   line 4: 35
   line 5: 40


}
プライマリ・バージョン番号、セカンダリ・バージョン番号、定数プール、javaコンパイラが自動的に追加したデフォルトのコンストラクタのバイトコード、mainメソッドのバイトコードが表示されます.
終わりの言葉
このブログでは、定数プールとは何か、実行時定数プールとは何か、String#intern()メソッドは何かという問題を取り上げ、一例を挙げてjavaバイトコード命令を引き出した.次の文章では、定数プール実行時定数プールString internメソッドが思いついた(二)javaバイトコードの基本知識を学び、バイトコード命令とスタック深さを逐句分析する.