우리는 지난 포스팅을 통해 Java의 String은 왜 불변인가에 대해 알아보고, Java에서는 String을 String constant pool에서 관리한다는 것을 알아보았습니다.
2019/12/19 - [JAVA] - Java의 String 이야기(1) - String은 왜 불변(Immutable)일까?
이번에는 바로 그 String Pool에 대해 조금 더 자세히 알아보도록 하겠습니다.
앞서 언급한 것과 같이 Java에서는 String을 String Pool이라는 영역을 따로 두어 그곳에 저장을 합니다. 그리고 String Pool은 Heap Memory 영역에 있습니다.(Java 7 버전 이상)
Java에서 String은 조금 특별한 클래스입니다.
왜냐하면 다른 클래스와 마찬가지로 new 키워드와 생성자를 사용해 객체를 생성할 수 있을 뿐만 아니라, 생성자 없이 큰 따옴표(")를 통해서도 객체를 만들 수 있기 때문입니다.
하지만 여기서 주의할 점은, new 키워드를 통해서 만드는 String 객체와 큰 따옴표(")만을 사용해 생성하는 String 객체에는 조금 차이가 있다는 것입니다.
우선 이 둘을 비교하기에 앞서 Java에서 String Pool을 어떻게 관리하는지 먼저 살펴보겠습니다.
String s1 = "Cat";
String s2 = "Dog";
String s3 = new String("Cat");
간단한 코드입니다.
String 변수 s1과 s2는 큰 따옴표(")를 통해 선언하였고, s3는 생성자를 사용해 선언하였습니다.
그러면 도대체 큰 따옴표를 통해 생성하는 것과 생성자를 통해 생성하는 것에 어떤 차이가 있는지 다음 그림을 통해 확인해보도록 하겠습니다.
보시다시피 큰 따옴표를 사용하여 선언한 s1과 s2는 String pool의 메모리를 가리키고 있습니다.
반면에 생성자를 통해 생성된 s3는 String pool이 아닌 일반 객체처럼 힙(Heap) 영역에 할당된 메모리를 가리키고 있습니다.
이를 어떻게 확인할 수 있을까요? 다음 코드를 통해 확인해보도록 하겠습니다.
String s1 = "Cat";
String s2 = "Cat";
String s3 = new String("Cat");
String s4 = new String("Cat");
System.out.println(s1 == s2); // true
System.out.println(s1 == s3); // false
System.out.println(s3 == s4); // false
(참고로 == 연산자는 메모리 주소를 비교하게 됩니다. 위 코드는 메모리 주소에 대한 비교를 위해 == 연산자를 사용한 것이기 때문에, String의 값을 비교하실 때에는 항상 equals() 메소드를 사용하여 비교하셔야 합니다.)
위 코드의 결과는 차례대로 true, false, false를 출력합니다.
왜 그럴까요?
첫 번째 s1 == s2 는 s1과 s2가 모두 큰 따옴표(")를 사용해 선언되었으므로 String pool 안에 "Cat"이 할당된 같은 메모리를 가리키게 됩니다.
두 번째 s1 == s3 는 s1은 String pool의 메모리를, s3는 Heap 영역에 동적으로 할당된 메모리를 가리키기 때문에 서로 다른 메모리를 가리켜 결과로 false를 나타냅니다.
세 번째 s3 == s4 는 s3와 s4 모두 String pool이 아닌 Heap 영역에 동적으로 할당된 메모리를 가리키지만, 각자 본인의 메모리를 별도로 할당받기 때문에 결과로 false를 나타냅니다.
String Pool은 앞선 포스팅에서 다룬 것과 같이 Java에서 String이 불변(Immutable)이기 때문에 가능합니다.
String Pool과 플라이웨이트(Flyweight) 패턴
String Pool은 플라이웨이트 디자인 패턴(Flyweight Design Pattern)을 잘 적용한 대표적인 사례입니다.
덕분에 String Pool은 Runtime에서 많은 양의 String 객체를 생성하게 되더라도 많은 양의 메모리 공간을 절약할 수 있습니다.
우리가 큰 따옴표(")를 통해 String 객체를 생성할 때, Java는 먼저 String Pool에 생성하고자 하는 값과 같은 값의 String 객체가 있는지 탐색합니다. 그래서 만약 String Pool에 있다면 새로운 객체를 생성하지 않고 찾은 주소값을 리턴하고, 없다면 새로운 객체를 만들어 String Pool에 할당한 뒤 그 주소를 리턴하게 됩니다.
만약 new 키워드를 통해서 생성하게 된다면 어떻게 될까요?
new 키워드는 String 객체를 강제로 Heap 영역에 생성하고 그 주소값을 리턴하도록 합니다. 하지만 그렇다고 해서 String Pool과 아주 관련이 없어지는 것은 아닙니다. 경우에 따라서 new 키워드로 만드는 String 객체는 Heap 영역에 하나, String Pool에도 하나 총 2개의 객체를 생성할 수도 있습니다.
String str = new String("Cat");
예를 들어, 위 코드와 같이 "Cat"이라는 값을 갖는 String 객체를 new 키워드와 함께 생성한다고 할 때 만약 이미 String Pool에 "Cat"이 있는 상태였다면 위 코드는 그저 Heap 영역에서 한 번만 객체를 생성하게 됩니다. 하지만 String Pool에 "Cat"이 없는 상황이라면 new String("Cat") 은 Heap 영역에도 하나, String Pool 영역에도 하나 총 2개의 String 객체를 생성하게 됩니다.
한 마디로, 어떠한 방법으로 String 객체를 선언하더라도 String Pool에는 생성된 값을 갖는 메모리는 반드시 생성됩니다.
그리고 new 키워드로 생성된 String 객체라 하더라도 intern() 메소드를 사용하면 String pool에서 같은 값을 갖는 메모리를 불러올 수 있습니다.
intern()을 사용한다면 다음과 같은 결과를 확인할 수 있습니다.
String s1 = new String("Cat");
String s2 = s1.intern();
String s3 = "Cat";
System.out.println(s1 == s2); // false
System.out.println(s2 == s3); // true
System.out.println(s1.intern() == s3); // true
이렇게 해서 String Pool에 대한 내용을 살펴봤습니다. String Pool에 대해 잘 이해하기 위해서는 Java에서 String이 불변이라는 것과 플라이웨이트 패턴이 무엇인지에 대해 알아야 합니다.
다음 포스팅에서는 String과 + 연산자, 그리고 String과 StringBuilder의 비교에 대한 내용을 다뤄보도록 하겠습니다.
'JAVA' 카테고리의 다른 글
[Java] 알아두면 쓸모있는 정렬된 Map - SortedMap(feat. TreeMap) (0) | 2020.02.02 |
---|---|
Java의 String 이야기(3) - String vs StringBuilder (0) | 2019.12.27 |
Java의 String 이야기(1) - String은 왜 불변(Immutable)일까? (1) | 2019.12.19 |
Java는 왜 클래스 간 다중 상속을 허용하지 않을까?- [Diamond Problem] (3) | 2019.10.23 |
[Java] 추상 클래스(Abstrac Class) VS 인터페이스(Interface) (0) | 2019.09.16 |