Java

[Java]String pool: String에서도 캐싱 기능을 제공한다고?

Woonys 2022. 6. 22. 09:34
반응형

Intro: String에서 객체 비교

예제를 하나 만들어보자.

//case 1
String name1 = new String("BHK");
String name2 = new String("BHK);

boolean result1 = name1.equals(name2) 
boolean result2 = name1 == name2 // 

위에서 공부한 내용과 같이 결과는 다음과 같다.

true // .equals()    name1, name2 값만 비교
false // ==          name1, name2의 객체 주소를 비교

아래 예제는 어떨까?

//case 2
String name1 = "BHK";
String name2 = "BHK";

System.out.println(name1 == name2);

결과는 참이다.

true

어떻게 이럴 수 있지? 저번 글을 공부하고서 이걸 본다면 “아하, String도 캐싱 기능을 제공하는 것이구나!”라고 자연스럽게 떠올릴 수 있을 것이다. 그렇다. String 객체에서 역시 일종의 캐싱 기능을 제공하는 것. <이것이 자바다> 5장에서는 이렇게 설명한다.

"자바는 문자열 리터럴이 동일하다면 String 객체를 공유하도록 되어 있다. name1과 name2 변수가 동일한 문자열 리터럴을 참조할 경우 name1과 name2는 동일한 String 객체를 참조하게 된다.”

case2처럼 문자열 리터럴을 사용해 변수에 문자열을 저장한 경우에는 위의 인용구와 같이 동일한 String 객체를 공유한다. 두 문자열이 동일하기 때문. 반면, new 연산자를 사용해 직접 String 객체를 생성할 경우에는(case 1)힙 영역 내 서로 다른 주소 번지에 두 String 객체를 생성한 경우가 되어 false를 반환하는 것이다.

String Pool: 자바는 동일한 String을 어떻게 관리하지?

다시 위의 인용구를 보자. “문자열 리터럴이 동일하다면 String 객체를 공유하도록 되어 있다”고 했다. 근데 이걸 어떻게 할 수 있을까? Integer는 -128~127 사이 값을 저장해두기로 미리 정해놨기에 이것이 가능했다. 그렇다면 String은? 어떤 문자열이 나올지 하늘도 모르고 땅도 모르고 나도 모르는데 어떻게 미리 정할 수 있을까? 이는 힙 메모리 내부에 String pool이라고 해서 가변 영역을 지정해놨기에 가능하다.

 

 

자바 힙 메모리 영역에는 String 리터럴을 저장해두는 String Pool 영역이 따로 있다. 문자열을 매번 객체로 할당하게 되면 시간 및 메모리 측면에서 비용 소모가 높다. 따라서 매번 문자열 리터럴이 생성될 때마다 JVM이 마련해둔 String pool을 체크한다. 이미 동일한 문자열이 존재할 경우, 해당 객체에 대한 참조를 반환하고 pool 내부에 동일한 문자열이 없을 때에만 새로운 String 객체를 초기화해 pool 내부에 생성한다.

 

그런데 위의 그림에서 s4, s5 케이스와 같이 (맨 처음에 적었던 case 1과 동일) 같은 문자열이더라도 new 연산자로 새 객체를 생성해버리면 힙 메모리 내 서로 다른 주소 번지에 위치하게 된다. 심지어 이는 new 연산자를 사용했기 때문에 기존의 string pool 안에도 들어가지 않는다.

Java의 역사: 처음부터 String pool을 제공했을까?

사실 자바에서 처음부터 String pool을 제공한 것은 아니었다. Java 8과 이전 버전 각각 메모리를 비교해보자.

 

 

자바 8 버전 이전에는 위에서 설명한 String pool PermGen(Permanent generation)이라고 하는 메모리 영역에서 관리했다. 근데 이 영역으로 인해 문제가 많았다. 여기는 런타임 시에 확장될 수 없을 뿐더러 GC의 영향 범위에도 벗어나 있다. 즉 고정된 메모리 크기인데 메모리 관리도 안되는 것. 그래서 너무 많은 String을 할당하면 OutOfMemory 에러를 야기하게 된다(고정 크기이기 때문). 그래서 8버전 이상부터 이를 Native 메모리 영역에 위치한 Metaspace로 옮긴 것. Native memory는 OS에서 자체적으로 관리하는 메모리 영역이기에 가변적으로 사용할 수 있기 때문이다.

Further Question

근데 아직 풀리지 않은 궁금증이라면.. 진작에 PermGen이 아닌 곳에 String pool을 배치했으면 되지 않았을까..하는 궁금증이..(혹시 아시는 분께서는 꼭 댓글 달아주시면 감사드리겠습니다!)

Reference

반응형