-
Java String Pool이란?TECH/Java 2022. 11. 17. 20:54
String Interning
String의 불변성 덕분에, JVM은 String Pool 내에서 단일 문자열에 대해 단 하나의 복제본만 가짐으로서 String에 할당된 메모리를 최적화 할 수 있다. 이 과정을 interning이라고 한다.
String 변수를 만들고, 값을 할당할 때 JVM은 같은 값을 갖는 String 을 pool에서 찾는다.
발견한 경우 Java Compiler는 추가로 메모리를 할당하지 않고 간단히 메모리 주소에 대한 레퍼런스를 응답한다. 발견하지 못한 경우, pool에 String을 추가되고 (interned) 레퍼런스를 응답한다.
String constantString1 = "foo"; String constantString2 = "foo"; assertThat(constantString1) .isSameAs(constantString2);
Strings Allocated Using the Constructor
new 를 사용하여 String을 생성할 때, Java 컴파일러는 새로운 객체를 생성하고, 이를 JVM에 예약된 heap 공간에 저장한다.
이러한 방식으로 생성된 String은 자신만의 주소를 갖고 다른 메모리 영역을 가르킨다.
String constantString = "foo"; String newString = new String("foo"); assertThat(constantString).isNotSameAs(newString);
String Literal vs String Object
new() 를 사용해서 String 객체를 생성하면, 항상 힙에 새로운 객체를 생성한다. 반면, "foo"와 같이 String literal syntax를 통해 생성한 경우 String pool에서 이미 존재하는 객체를 응답하거나 새로운 객체를 생성하고 나중을 위해 pool에 저장한다.
상위 레벨에서, 두 객체는 모두 스트링 객체이지만 new() 는 항상 새로운 스트링 객체를 생성하고 literal을 통해 String을 생성할 경우 intern한다는 점이 가장 큰 차이점이다.
literal을 통해 생성된 객체와 new를 통해 생성된 객체를 비교해보자.
String first = "foo"; String second = "foo"; System.out.println(first == second); // True
String third = new String("foo"); String fourth = new String("foo"); System.out.println(third == fourth); // False
String fifth = "foo"; String sixth = new String("foo"); System.out.println(fifth == sixth); // False
일반적으로, 가능한 String literal을 사용해야 한다.
가독성이 더 좋고, 컴파일러가 코드를 최적화할 여지를 준다.
Manual Interning
intern() 메소드를 호출함으로서 String을 수동으로 intern할 수 있다. 수동으로 interning 하면 String은 자신의 레퍼런스를 pool에 저장하게 되고, JVM은 필요시 이 레퍼런스를 리턴하게 된다.
String constantString = "foo"; String newString = new String("foo"); String internedString = newString.intern(); Assertions.assertFalse(constantString == newString); Assertions.assertTrue(constantString == internedString);
Garbage Collection
Java 7 이전에는 JVM은 Java String Pool을 PermGen 영역에 위치시켰는데, 이는 고정된 사이즈를 가졌다. 런타임에 늘릴 수 없었고, Garbage Collection의 대상이 아니였다.
String을 PermGen (힙 대신)에 interning하는 것은 너무 많은 String을 intern 했을 경우 OOM을 발생시킬 위험성이 있었다.
Java 7 부터 Java String Pool은 힙 영역에 저장되어 garbage collection의 대상이 된다. 이 방법의 장점은 String이 pool에서 제거될 수 있기 때문에 메모리를 해제하여 OOM 에러를 줄일 수 있는 것이다.Performance and Optimizations
(Java7 이후 부터)
pool 사이즈 보기
-XX:+PrintFlagsFinal
-XX:+PrintStringTableStatistics
(bucket의 관점에서 ) pool 사이즈 늘리기
-XX:StringTableSize=4901
pool 사이즈를 늘리는 것은 더 많은 메모리를 사용하지만, String을 테이블에 넣을 때 소요되는 시간을 줄일 수 있다는 장점이 있다.
A Note About Java 9
Java9 부터 String의 내부 표현 방식이 Compact Strings로 변경되었다. 이로 인해 새로운 포맷은 char[]와 byte[] 사이에서 저장된 컨텐츠에 기반하여 적절한 인코딩 방식을 선택하게 된다.
새로운 String 표현 방식이 필요시에만 UTF-16을 사용하기 때문에, 필요한 힙 메모리의 양은 줄어들 것이고 이로 인해 JVM에서 Garbage Collector의 오버헤드도 줄어들게 된다.
원문
'TECH > Java' 카테고리의 다른 글
Singleton Pattern이란? (0) 2022.12.16 Managed Language 자바, 자바는 어떻게 힙에서 제거할 객체를 알아낼까? (0) 2022.11.29 Mono.zipWith 혹은 Mono.zipWhen이 호출되지 않을 때 (0) 2022.10.23 JVM 메모리 - heap, stack (0) 2022.08.07 Slack api 호출시 account_inactive 에러가 발생하는 경우 (0) 2022.02.05