序文
学習JVMのプロセスは、文字列のインターン()メソッドに遭遇し、その後、非常に体系的ではないことがわかったインターネット上でたくさん探して、文の多くは矛盾しています。だから私はこのレコードを勉強することにしました。
まず、intern()の意味を見てみましょう。
当面は、文字列定数プールに入れるだけです。
実践的進化
- JDK1.7:このメソッドを呼び出すと、まず文字列定数プールに移動して既に存在するかどうかを確認し、既に存在する場合は、文字列定数プール内のこの定数のアドレス値を直接返します。
- JDK1.7以降:このメソッドを呼び出すと、まず文字列定数プールに移動して既に存在するかどうかを確認し、既に存在する場合は、文字列定数プール内のこの定数のアドレス値を直接返し、存在しない場合は、文字列定数プール内のヒープに参照のコピーを保存し、そのアドレス値を返します。リテラルやnew String()で文字列を作成すると、定数プールはオブジェクトとして保存され、intern()メソッドでは、定数プールは参照として保存されます。以下はJDK7以降のバージョンで分析されたものです。
文字列を作成するいくつかの方法
- String str= "abc": リテラルで。まず、同じ文字列が定数プールに既に存在するかどうかを確認します。存在する場合は、定数プールにその文字列への参照を返します。存在しない場合は、定数プールに文字列オブジェクトを作成し、その参照を返します。
String str = new String("abc")
文字列オブジェクトを明示的に作成します。まずヒープにオブジェクトを作成し、同じ文字列が定数プールに既に存在するかどうかを確認し、存在する場合はヒープへの参照を直接返します。存在しない場合は、定数プールに文字列オブジェクトを作成し、ヒープへの参照を返します。String str = "abc" + "de"
定数スプライシング。String str = "abcde "と同等に最適化されてコンパイルされます。String str = new String("abc") + new String("de")
まず最初に、appendメソッドでStringBuilderオブジェクトを作成し、次にtoStringでStringに変換します。下は、appendメソッドでStringBuilderオブジェクトを作成し、最後にtoStringでStringに変換します。合計5つのオブジェクトが作成されます:ヒープ、文字列定数プール。これは、ループ本体でどの形式の文字列スプライシングが使用されても、多くのオブジェクトが生成され、メモリを浪費し、パフォーマンスに影響することを示しています。
intern()によって影響を受けるメモリ構造の完全な分析と4つの例
- eg1
@Test
public void function1() {
String str1 = new String("abc");
String str2 = "abc";
String intern = str1.intern();
System.out.println(str1==str2);//false
System.out.println("str1: "+System.identityHashCode(str1));//
System.out.println("str2: "+System.identityHashCode(str2));//
System.out.println("intern: "+System.identityHashCode(intern));//
}
String str1 = new String("abc")
ヒープと定数プールにオブジェクトを作成し、str1はヒープを指します。
文字列str2 = "abc" 定数プールで "abc "の参照を見つけ、定数プールの参照を直接返します。
String intern = str1.intern()
定数プールで "abc "の参照を見つけたので、定数プールの参照を直接返します。
- eg2
@Test
public void function2() {
String str1 = new String("abc");
String intern = str1.intern();
String str2 = "abc";
System.out.println(str1==str2);//false
System.out.println("str1: "+System.identityHashCode(str1));//
System.out.println("str2: "+System.identityHashCode(str2));//
System.out.println("intern: "+System.identityHashCode(intern));//
}
ここでは2行目と3行目の順番が変わっただけで、構造はeg1と全く同じです。
- eg3
@Test
public void function3() {
// String hel = new String("hel");
// String lo = new String("lo");
// String str1 = hel+lo;
String str1 = new String("hel") + new String("lo");
String intern = str1.intern();
String str2 = "hello";
System.out.println(str1 == str2);//true
System.out.println("str1: " + System.identityHashCode(str1));//
System.out.println("str2: " + System.identityHashCode(str2));//
System.out.println("intern: " + System.identityHashCode(intern));//
// System.out.println(System.identityHashCode(hel));//
// System.out.println(System.identityHashCode(hel.intern()));//
// System.out.println(System.identityHashCode(lo));//
// System.out.println(System.identityHashCode(lo.intern()));//
}
String str1 = new String("hel") + new String("lo");
コメントを解放すると、オブジェクトのメモリアドレスの詳細なハッシュコードが表示されます。ヒープ、文字列定数プールに:5つのオブジェクトの合計を作成します。
String intern = str1.intern();
定数プールでは "hello "の参照を見つけられなかったので、定数プールでhelloオブジェクトの参照のコピーを保存し、参照アドレスを返します。
文字列str2 = "hello";定数プールでは "abc "参照で見つかったので、直接参照の定数プールに戻ります。str1はヒープへの参照を指し、internとstr2はヒープへのstr1の参照のコピーを指すので、これらは等しいです。
- eg4
@Test
public void function4() {
// String hel = new String("hel");
// String lo = new String("lo");
// String str1 = hel+lo;
String str1 = new String("hel") + new String("lo");
String str2 = "hello";
String intern = str1.intern();
System.out.println(str1 == str2);//true
System.out.println("str1: " + System.identityHashCode(str1));//
System.out.println("str2: " + System.identityHashCode(str2));//
System.out.println("intern: " + System.identityHashCode(intern));//
// System.out.println(System.identityHashCode(hel));//
// System.out.println(System.identityHashCode(hel.intern()));//
// System.out.println(System.identityHashCode(lo));//
// System.out.println(System.identityHashCode(lo.intern()));//
}
String str1 = new String("hel") + new String("lo");
str1はヒープを指しています。
文字列str2 = "hello";定数プール内の "abc "への参照が見つからなかったので、オブジェクトを作成し、定数プールへの参照を返します。
String intern = str1.intern();
定数プールで "hello "の参照を見つけたので、直接参照アドレスを返します。str1はヒープへの参照を指し、インターンとstr2は定数プール内のstr1への参照を指すので、str1とstr2は等しくありません。
JDKでStringに加えられた変更
つの質問
文字列定数プールとインターン・メソッドの終わりを見れば、一定の理解が得られるはずです。しかし、ここでまだ2つの問題があります:
1.eg3の例では、文字列が "java"、"12"、"1122 "や他の特殊な文字列の例は保持されません。推測:定数プールがこれらの文字列に既に存在する場合、jvmの初期化。
2.単一のテストと同じコードを実行するメインスレッドを通して、結果は同じではありません。チェックした後、どのような魔法の値?
public static void main(String[] args) {
String s3 = new String("1") + new String("1");
s3.intern();
String s4 = "11";
System.out.println(s3 == s4);//true
}
@Test
public void fun4() {
String s3 = new String("1") + new String("1");
s3.intern();
String s4 = "11";
System.out.println(s3 == s4);//false
}
この時点で本当に理解できないし、そのやり取りを共有したいと思っているので、興味があれば調べてみてください。