Stringとheap領域とconstant pool

# javaのメモリ領域

ざっくり！

<dl>
<dt>レジストリ</dt>
<dd>cpuに存在領域で、制御できない</dd>

<dt>stack領域</dt>
<dd>基本型、参照値が保存される</dd>
<dd>メモリ管理は、ポイントを前後に移動することで行う</dd>

<dt>heap領域</dt>
<dd>newで生成されたオブジェクト</dd>

<dt>static領域</dt>
<dd>言葉のとおり、staticメンバー</dd>

<dt>constant pool</dt>
<dd>コンパイル時確定され、.classに存在するデータ + ほか</dd>

<dt>非RAM領域</dt>
<dd>hdd等</dd>

</dl>

# Stringとheap領域とconstant pool
本題：
「コンパイル時確定され、.classに存在するデータ」について検証してみたい。

## よく知ってる内容の検証

### constant poolに生成される
```java
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領域に生成される
```java
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領域に生成される
```java
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("")
```java
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になる。

### 定数 + 参照値
```java
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が生成される。
2. a2は参照値なので、コンパイル時には確定されない。
よって、実行時にheap領域に"ab"が生成、a3が指す。
3. 結果、a1はconstant pool,a3はheap領域なので、falseになる。

### 定数 + final
```java
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に値を生成し、その参照値を返す。

```java
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の+がパフォーマンスがよくない原因
```java
String s = null;
for(int i = 0; i < 100; ++i) {
    s = s + "a";
}
```
今までの検証を元に、
`s + "a";`のsは変数なので、実行する度に新しいオブジェクトが生成と破棄が行われからだね。