Java String constant pool

Java String이 메모리에 저장되는 방식

String이 메모리에 저장되는 방식

자바에서 String을 생성하는 데에는 두 가지 방식이 있다.

두 가지 선언 방식의 차이점은 아래와 같다.

new 선언 리터럴 선언
저장 장소 “Heap” 영역에 저장 “String constant pool"에 저장
속도 Literal 선언보다 느리다. new 선언보다 빠르다
동일한 문자열 비교시 .equals 메소드로 비교해야 같은 결과 == 연산자로 비교 가능

“String constant pool"에 문자열이 존재하면 해당 주소값을 반환, 존재하지 않는다면 새로 저장하고 새로운 주소값을 반환. string constant pool은 Perm 영역이라는 곳에 존재하는데 Java7을 기점으로 위치가 변경되었다.

동일한 문자열을 위의 두 가지 방식으로 생성하여 == 연산자와 .equals()로 비교해보면 아래와 같은 결과가 나온다.

public static void main(String[] args) {
    String a = "apple";
    String b = "apple";
    String c = new String("apple");
    
    System.out.println(a == b); // true
    System.out.println(a == c); // false
    
    System.out.println(a.equals(b)); // true
    System.out.println(a.equals(c)); // true
}

String constant pool 위치 변경

Java6까지 String constant pool의 위치는 Perm 영역이었다. Perm 영역에 위치하였던 게 Java7에서 Heap 영역으로 변경되었다. 그 이유는 OutOfMemoryException 문제 때문이다.

Perm 영역은 고정된 사이즈고 Runtime에 사이즈가 확장되지 않는다. Perm 영역의 사이즈를 늘릴 수는 있지만 어쨌거나 Runtime에 사이즈가 변경되는 것은 아니다. 그래서 Java6까지는 String의 intern() 메서드를 호출하는 것은 OutOfMemoryException을 발생시킬 수 있고 그 부분을 컨트롤할 수 없었기 때문에 거의 사용하지 않는 것이 맞다.

그래서 Oracle의 엔지니어들이 Java7에서 Perm 영역이 아닌 Heap 영역으로 string constant pool의 위치를 변경하였다. Heap 영역으로 변경함으로써 얻는 이점이 무엇일까? 바로 string constant pool의 모든 문자열도 GC의 대상이 될 수 있다는 점이다.

String constant pool의 사이즈를 지정할 수 있는데 -xx:StringTableSize 옵션으로 설정할 수 있다. 여기에는 1,000,000와 같은 숫자가 아닌 1,000,003과 같은 소수를 사용해야 한다. hashCode 성능과 관련된 부분인데 Java Performance Tuning Guide에서 이 아티클에 자세한 내용이 나와있다.

intern() 메서드를 적극적으로 사용한다면 -xx:StringTableSize의 기본값 (1009) 보다 높게 설정해야 한다. 그렇지 않으면 Linked List 수준의 성능으로 떨어진다고 한다.

참조