String Constant Pool

같은 내용의 문자열 리터럴을 하나의 객체로 공유하기 위해 JVM이 Heap 영역에 따로 마련해둔 저장 공간입니다.

리터럴 생성과 생성자 생성의 차이

String first = "hello";              // 리터럴 — Pool에 저장
String second = new String("hello"); // 생성자 — Heap에 새 객체
String third = "hello";              // Pool에 이미 있으므로 재사용

System.out.println(System.identityHashCode(first));  // 498931366
System.out.println(System.identityHashCode(second)); // 2060468723
System.out.println(System.identityHashCode(third));  // 498931366

리터럴로 만든 문자열은 Pool에 등록되고, 같은 문자열이 이미 있으면 그 주소를 그대로 참조합니다. 반면 new String()은 내용이 같더라도 항상 새로운 객체를 Heap에 만듭니다.

그래서 first == third는 true지만 first == second는 false입니다. 내용 비교는 항상 equals()를 써야 하는 이유입니다. (동일성과 동등성 참고)

intern()

intern()은 Heap에 있는 String 객체를 Pool에 등록하고 Pool의 참조를 돌려줍니다. 이미 같은 문자열이 Pool에 있으면 그 주소를 반환합니다.

String first = "hello";
String second = new String("hello");
String third = second.intern();

System.out.println(System.identityHashCode(first));  // 498931366
System.out.println(System.identityHashCode(third));  // 498931366  ← first와 동일

왜 필요한가

문자열은 프로그램에서 가장 많이 쓰이는 값입니다. 같은 문자열이 수백 번 등장할 때마다 객체를 새로 만들면 메모리 낭비가 큽니다. Pool은 이를 공유해 메모리를 절약합니다.

이 공유가 안전한 이유는 String이 불변 객체이기 때문입니다. 여러 참조가 같은 객체를 가리켜도 누구도 그 내용을 바꿀 수 없으므로 서로 영향을 주지 않습니다. 만약 String이 가변이었다면 한쪽에서 값을 바꿨을 때 그 문자열을 공유하던 모든 코드가 영향을 받았을 것입니다.

주의사항

핵심 정리

→ String 객체는 가변일까요, 불변일까요? 그렇게 생각하신 이유도 함께 설명해 주세요

CSLOG에서 보기