본문 바로가기
Java

Java - String pool

by 오늘부터개발시작 2022. 8. 3.

알고리즘 문제를 풀다보면 이런 이야기를 한 번쯤 본 적이 있을 것이다. 루프문에서 문자열이 많이 사용되면 StringBuilder를 사용해라. 적은 시간, 메모리로도 틀렸다는 결과를 받을 수 있기 때문에 최소한의 메모리와 시간을 사용하는 것이 중요하기 때문이다. 문자열을 계속해서 concatenate하는 것은 메모리 상 엄청난 낭비가 된다. 다른 객체들과 다르게 String은 Pool을 가지고 있기 때문이다. 

 

작동 방식

String s = "Hello";
s += " World!";

위처럼 String에 World!를 더하면 어떻게 될까? 기존의 객체들에 새로운 값을 주면 객체의 값이 변경될 뿐이다. 그런데 String에 새로운 값을 주면 기존의 값이 사라지지 않고 Pool에 남아있게 된다. 즉 String Pool에는 Hello와 Hello World! 2개 값이 남아 있다는 의미이다. 그렇기 때문에 for문에서 concatenating을 계속하게 되면 엄청나게 많은 개수가 String Pool에 생길 수가 있다.

 

그렇다면 만약 Hello라는 String 변수를 하나 더 선언하게 되면 어떻게 될까? 이 때는 String Pool에 새로운 값이 생기지 않고 기존에 존재하던 Hello의 값을 가져다 준다. 값을 가져다 준다기보다는 새로 선언한 String도 String Pool의 Hello라는 값을 참조시킨다. 이런 구조기 때문에 String 변수에 새로운 값을 줬을 때 값 자체를 바꿔버리면 이미 참조하고 있는 다른 값들도 바뀌게 되어 문제가 생긴다. 그래서 String Pool에 새로 저장하게 되는 것이다. Java에서는 String interning이라는 과정을 사용해서 String pool에서 값이 이미 존재하는지 확인하고 값을 리턴해준다. String interning은 Java에서 나온 개념은 아니고 컴퓨터 공학에 존재하는 용어인데 Java에서 이를 사용해 구현한 것이라고 보면 된다. 프로그래밍을 하면서 한 번도 써보지 않았지만 String class에는 intern()이라는 메소드가 존재한다. intern() 메소드는 equals() 메소드보다 빠르게 동작해서 데이터가 엄청나게 많을 때 사용한다고 한다. 

 

Intern

String a = "abc";
String b = "abc;
String c = a.intern();

 

위의 코드에서 b = "abc"와 c = a.intern()과 같은 코드라고 보면 되는데 b = "abc"를 하면 string interning 과정을 거쳐서 "abc"를 리턴해 주기 때문에 a.intern()과 동일하다. 여기서 a,b,c에 == 연산자를 대입해보면 참조하는 주소값이 같기 때문에 같다고 true를 받을 수 있다. 

 

그렇다면 new를 사용해서 String 변수를 만들면 어떻게 될까? new를 사용하면 값이 String pool이 아니라 새로운 객체를 참조하게 되기 때문에 == 연산자 비교를 하면 틀리다고 나온다. 

 

위치

String Pool이 존재하는 곳은 Java 7부터 Heap이다. 기존에는 PermGen 영역에 존재하고 있었지만 Runtime에 PermGen이 확장될 수 없기 때문에 String 값이 너무 많아지면 OOM(Out of Memory) Exception이 발생할 확률이 있어서 변경되었다고 한다.