LoginSignup
49
40

More than 5 years have passed since last update.

Stringとheap領域とconstant pool

Last updated at Posted at 2017-11-06

javaのメモリ領域

ざっくり!

レジストリ
cpuに存在領域で、制御できない
stack領域
基本型、参照値が保存される
メモリ管理は、ポイントを前後に移動することで行う
heap領域
newで生成されたオブジェクト
static領域
言葉のとおり、staticメンバー
constant pool
コンパイル時確定され、.classに存在するデータ + ほか
非RAM領域
hdd等

Stringとheap領域とconstant pool

本題:
「コンパイル時確定され、.classに存在するデータ」について検証してみたい。

よく知ってる内容の検証

constant poolに生成される

String a1 = "aaa";
String a2 = "aaa";
System.out.println(a1 == a2); // true
  1. contant pool領域に"aaa"が生成され、stack領域に"aaa"を指す参照値a1が生成される。
  2. contant pool領域に"aaa"があるので、stack領域に"aaa"を指す参照値a2が生成される。
  3. a1,a2とも"aaa"のアドレスが入っているので、trueになる。

heap領域に生成される

String a3 = new String("aaa");
String a4 = new String("aaa");
System.out.println(a3 == a4); // false
  1. heap領域に"aaa"が生成され、stack領域に"aaa"を指す参照値a3が生成される。
  2. heap領域に"aaa"が生成され、stack領域に"aaa"を指す参照値a4が生成される。
  3. a3,a4は別々の"aaa"アドレスが入っているので、falseになる。

constant pool, heap領域に生成される

String a5 = "aaa";
String a6 = new String("aaa");
System.out.println(a5 == a6); // false
  1. constant pool領域に"aaa"が生成され、stack領域に"aaa"を指す参照値a5が生成される。
  2. heap領域に"aaa"が生成され、stack領域に"aaa"を指す参照値a6が生成される。
  3. a5,a6は別々の"aaa"アドレスが入っているので、falseになる。

ちょっと変わった内容の検証

定数 + 定数

String a1 = "ab";
String a2 = "a" + "b";
System.out.println(a1 == a2); // true
  1. constant pool領域に"ab"が生成され、stack領域に"ab"を指す参照値a1が生成される。
  2. "a"は定数、"b"は定数、よってコンパイル時にa2 = "ab"になる。
    constant pollに存在する"ab"を指す参照値a2が生成される。
  3. a1,a2とも"ab"のアドレスが入っているので、trueになる。
  4. decompileで覗いてみると,String a2 = "ab";だった。

定数 + new String("")

String a1 = "ab";
String a2 = "a" + new String("b");
System.out.println(a1 == a2); // false
  1. constant pool領域に"ab"が生成され、stack領域に"ab"を指す参照値a1が生成される。
  2. new String("")はコンパイル時には確定されない。 よって、実行時にheap領域に"b"が生成、さらにheap領域"ab"が生成される。
  3. 結果、a1,a2は別々の"ab"アドレスが入っているので、falseになる。

定数 + 参照値

String a1 = "ab";
String a2 = "b";
String a3 = "a" + a2;
System.out.println(a1 == a3); // false
  1. constant pool領域に"ab"が生成され、stack領域に"ab"を指す参照値a1が生成される。
  2. constant pool領域に"b"が生成され、stack領域に"ab"を指す参照値a2が生成される。
  3. a2は参照値なので、コンパイル時には確定されない。 よって、実行時にheap領域に"ab"が生成、a3が指す。
  4. 結果、a1はconstant pool,a3はheap領域なので、falseになる。

定数 + final

String a1 = "ab";
final String a2 = "b";
String a3 = "a" + a2;
System.out.println(a1 == a3); // true
  1. finalを付けると、a2は定数と見なされ、a3は定数 + 定数 = 定数になる。

decompilerで見ると、下記にコンパイルされた。

String a1 = "ab";
String a2 = "b";
String a3 = "ab";
System.out.println(a1 == a3);

String.intern()

constant poolを拡張できるメソッド。

Stringインスタンス.intern()した場合、
1. 同じ値がconstant poolに存在する場合、その参照値を返す。
2. 同じ値がconstant poolに存在しない場合、constant poolに値を生成し、その参照値を返す。

String a1 = "ab";
String a2 = new String("ab");
System.out.println(a1 == a2); // false
a2 = a2.intern();
System.out.println(a1 == a2); // true
  1. constant pool領域に"ab"が生成され、stack領域に"ab"を指す参照値a1が生成される。
  2. heap領域に"ab"が生成され、stack領域に"ab"を指す参照値a2が生成される。
  3. falseになるのは略。
  4. a2.intern()かつ"ab"がcontant poolに存在するので、constant poolの"ab"アドレスをa2に返す。
  5. a1, a2ともcontant poolの"ab"を指すので、trueになる。

おまけ:Stringの+がパフォーマンスがよくない原因

String s = null;
for(int i = 0; i < 100; ++i) {
    s = s + "a";
}

今までの検証を元に、
s + "a";のsは変数なので、実行する度に新しいオブジェクトが生成と破棄が行われからだね。

49
40
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
49
40