- Published on
String Contstant Pool
- 글쓴이
String은 어디에 저장될까?
자바에서 String은 불변(immutable)이다. 즉, 한번 생성된 String은 변경할 수 없다. 동일한 String은 단 하나만 저장된다. JVM의 메모리 최적화에 유리한 특징이다. 그렇다면 String은 어디에 저장될까?
String firstName = "Tina";
바로 String Pool이라는 공간에 저장된다.
String Pool이라는 특이한 공간
String Pool은 String Constant Pool, Constant Pool 등으로도 불린다.
이는 JVM의 특별한 영역으로, String 객체들이 저장되는 공간이다. String Pool은 Heap 영역에 존재한다. JDK 1.7 이전에는 PermGen 영역에 위치했지만, 이후 버전에서는 Heap 영역으로 이동되었다.
String이 PermGen 영역에 저장될때는 OutOfMemoryError: PermGen space
에러가 발생할 위험성이 있었기 때문이다. PermGen 영역은 JDK 1.8에서 완전히 제거되었으며, 메모리 이슈 리스크가 낮아졌다.
String Pool의 존재 이유는 성능 최적화와 메모리 효율성 때문이다. 불변성을 가진 String이 동일한 값을 가질 경우, 이를 재사용함으로써 메모리를 절약할 수 있다.
String firstName = "Tina";
String firstName2 = "Tina";
위의 코드에서 str1과 str2는 각각 "Tina"라는 값을 가지지만, 실제로는 String Pool에서 동일한 객체를 참조한다.
String Interning
스트링 인터닝(String Interning)은 String Pool에 String을 저장하고, 동일한 String이 요청되면 이미 Pool에 있는 String을 반환하는 프로세스를 말한다. String.intern() 메소드를 통해 수행되며, String Pool에 동일한 값이 있으면 해당 String을 반환한다.
아래는 String Object와 intern() 메소드를 사용해 강제로 String Pool에 저장하는 예제이다.
String str1 = new String("Tina").intern();
String str2 = "Tina";
System.out.println(str1 == str2); // true
"Tina"
와 new String("Tina")
는 동일할까?
String str = "Tina";
은 리터럴을 사용하여 문자열을 생성했다. 리터럴 문자열은 Heap 중 특별한 메모리 영역인 String pool에 저장된다. 이 방법으로 생성된 문자열은 하나의 주소를 가리키기 때문에 메모리 효율성을 높일 수 있다.
String str = new String("Tina");
는 새로운 문자열 객체를 명시적으로 생성한다(String Object). 이렇게 생성된 문자열은 일반 Heap 영역에 저장된다. new
키워드를 사용하면 항상 새로운 객체가 생성된다. String pool에 있는 기존 "Tina" 문자열과 별개의 객체를 생성하기 때문에 불필요한 메모리를 추가로 사용하게 된다.
String Literal과 String Object는 저장되는 공간이 다르다.
String Concatenation
+
연산자눈 두 문자열을 이어붙이는 가장 간단한 방법이다. 직관적이지만, 문자열을 이어 붙일때마다 새로운 String 객체를 만들기 때문에 concat 연산이 많아지는 경우엔 성능 이슈를 일으킬 수 있다.
StringBuilder와 StringBuffer를 이용하면 보다 효율적으로 concat 연산을 할 수 있다. StringBuilder와 StringBuffer는 내부적으로 가지고 있는 문자 배열을 확장하여 문자열을 추가한다.
StringBuffer는 thread-safe하지만, StringBuilder는 그렇지 않다.
+
연산자는 무조건 쓰면 안되는걸까?
JDK 6 이전에는 +
연산자를 사용한 concat연산이 많은 성능 문제를 일으켰다. 이는 +
연산자를 사용할 때마다 새로운 String 객체가 생성되고, 이전 객체는 가비지 컬렉션 대상이 되기 때문이다. 하지만, Java 6 이후로는 JVM이 +
연산자를 내부적으로 StringBuilder, StringBuffer를 이용한 테크닉을 사용하는 것으로 변환해 성능을 개선했다. 그러나 반복적인 concat 연산이 이루어 질 때는, 여전히 StringBuilder나 StringBuffer를 사용하는 것이 더 효율적이다.
To increase the performance of repeated string concatenation, a Java compiler may use the StringBuffer class or a similar technique to reduce the number of intermediate String objects that are created by evaluation of an expression.