[Java] String과 String Constant Pool
Java

[Java] String과 String Constant Pool

728x90

String은 자바에서 자주 쓰이는 자료구조이자 어떻게 보면 특별하 자료구조인데요!

오늘은 자바의 String에 대해서 살짝 깊게 다뤄보겠습니다~!!


String 이란?

먼저 프로그래밍 언어에 제약되지않은 String이라는 개념에 대해서 설명해보려고 합니다. 너무도 당연한 얘기지만 자바에서 String을 설명하려면 꼭 필요하기 때문입니다.

 

String은 한국어로 문자열이라고도 부르는데요, 여러 문자(Character)들이 모여서 하나의 집합으로 이루어진것을 우리는 문자열이라고 부릅니다.

 

그래서 그런지 자바 이전에 등장했던 C 에서는 String이라는 자료구조가 따로 존재하지 않습니다. 문자열을 사용해야할때는 char* (포인터) 또는 char[] (배열)의 형태로 문자열을 간접적으로 나타내죠. C++ 에서는 string 자료구조가 만들어지기는 했지만 결국 인덱싱 연산자([])를 오버로딩한거여서 char[] 와 크게 다르지 않습니다. string str = "abc"으로 선언해도  str[0] 이런식으로 접근이 가능합니다.

 

하지만 자바에서 String은 특별합니다! 지금부터 자바의 String 클래스의 특별함을 소개하겠습니다.


자바의 String 은 불변(immutable)하다

우선 첫번째로 자바에서 String이란 java.lang 패키지 안에 있는 String 클래스의 객체를 의미합니다. 그리고 자바의 모든 String은 불변(immutable)합니다. 한번 생성이 되면 다시는 수정이 될 수 없습니다.

 

웃기시네 ㅋㅋㅋ String끼리 붙이고 자르고 할 수 있거든요?? 자알못이세요?

만약 이미 만들어진 String에 + 연산사로 새로운 String을 만들게된다면, 이미 있는 String 객체의 값이 변하는게 아니라 새로운 String 객체가 메모리에 생겨나게됩니다! 따로 직접 생성된 String 값을 반복적으로 수정하고싶다면 StringBuilder 객체를 사용하는것이 추천됩니다.

 

아니 String 을 쓸일이 얼마나 많은데 그럼 왜 불변(immutable)으로 만들어요 불편하게??

그 이유는 여러가지가 있지만 대표적으로는 3가지가 있습니다!

  • 캐싱
    • 아래에서 추가적으로 설명하겠지만, 자바에서 String은 String Constant Pool이라는 영역에 따로 저장되고 여러 객체 또는 변수에서 공유하여 사용할 수 있습니다. 계속해서 같은 String을 만들지 않고 이미 만들어진 String (캐싱된 String)을 사용할 수 있게되는 것이죠.
  • 동기화
    • 불변한 객체는 멀티쓰레드(multi-thread)환경에서 동기화문제가 발생하지 않기 때문에 안전합니다.
    • String은 굉장히 자주 사용되는 객체이기 때문에 Thread-safe한것이 중요합니다.
  • 보안적 이유
    • DB 커넥션등 여러 상황에서 계정정보(아이디, 비밀번호)를 통해 인증을 수행합니다. 그리고 이 계정정보는 주로 String으로 이루어져있죠. String이 mutable(변화가능)하다면 해커의 공격으로 String값이 바뀔수있는 여지를 줄 수 있습니다.

자바의 String은 특별한 공간에 저장된다 (String Constant Pool)

자바바이트 코드를 실질적으로 실행시켜주는 JVM에 대해서 기억하시나요?

**혹시 JVM의 구조가 가물가물하시다면 잠깐 멈추고 여기를 먼저 보고와주세요!

https://jiwondev.tistory.com/114

저번 글에서 메서드 영역에는 런타임 상수풀, Runtime Contstant Pool (줄여서 Constant Pool)이 존재한다고 했었습니다. 이 공간은 말그대로 런타임에 생성되는 Static 상수 저장소입니다! 여기에는 String 뿐 아니라, 모든 종류의 숫자, 문자열, 식별자 이름, Class 및 Method에 대한 참조같은 값이 포함됩니다. 그리고 Constant Pool은 메소드영역 (Static 영역)에 있는 메모리이기때문에 GC의 대상이 아닙니다.

 

오 그럼 String도 이 Constant Pool에 저장되는건가요??

음.. 틀린...말은 아니지만! 엄밀히 말하면 String 타입의 상수를 선언했을 때는 여기에 저장이되지만, 상수로 저장하지 않으면 여기에 저장되지 않습니다. 우리가 개발을 하면서 String 객체를 사용할때는 보통 상수보다는 리터럴(Literal)로 생성을 하게 되고, 이때는 Constant Pool이 아니라 String Constant Pool에 따로 저장됩니다!

팁) String Constant Pool은 여러 이름으로 불립니다.
- String Pool
- String Constant Pool
- String Literal Pool
- String Intern Pool
위 4개는 모두 같은 것을 의미합니다. String Pool이 가장 많이 쓰이고 그다음 String Constant Pool이라고 많이 불리는것 같습니다!

 

리터럴(Literal)이 뭐에요?? 전 String 사용하면서 그런거 쓴적 없는데요;;

아마도 항상 쓰고계셨을겁니다 ㅋㅋㅋㅋ String 객체를 생성하는 두가지 방법이 있습니다

  • String 리터럴(Literal)사용: String str = "DannyJae"; (쌍따옴표를 이용하여 초기화)
  • String 생성자 사용: String str = new String("DannyJae");

언뜻보면 두가지 방식은 같은 결과를 줄 것처럼 생겼지만 아닙니다. 한번 볼까요?

// 리터럴 방식
String a = "aaaa";
String b = "aaaa";
System.out.println(a == b); // true

// 생성자 방식
String aa = new String("bbbb");
String bb = new String("bbbb");
System.out.println(aa == bb); // false

== 연산자로 확인을 하는 것은 동일성을 확인하는 것이죠. 그리고 동일성은 확인한다는 것은 이 객체가 같은 메모리 주소에 저장이 돼있는 것을 확인한다는 것입니다.

**혹시 동일성과 동등성의 차이를 더 자세히 알고싶으시면 여기를 먼저 보고와주세요!

 

그리고 결과는..!  리터럴로 생성했을때는 true, 생성자로 생성했을때는 false입니다.

같은 == 연산자를 사용했는데 왜 결과가 다를까요? 이 이유는 String Constant Pool에 대해 설명하면서 말씀드리겠습니다!


String Constant Pool

위에서 리터럴을 사용하면 String Constant Pool에 저장이 된다고했었죠! 만약 String을 리터럴 방식으로 초기화 시키게된다면 이 String Constant Pool에 저장이 되는데, 만약 새로운 변수에 같은 String 리터럴로 초기화하면 String Constant Pool에 새로운 String이 생겨나는게 아니라 같은 String(String이 저장된 주소)을 바라보게 됩니다.

 

이 때문에 리터럴을 사용해서 생성한 String을 비교를하면 == 연산자를 사용했을때도 같은 String 주소를 바라보기 때문에 true를 반환하게 되는것입니다! 이와 다르게 new String() 생성자를 사용하면 보통의 객체가 생성되듯이 항상 Heap 영역에 다른 메모리주소에 생성이 되기 때문에 == 연산자로는 false를 반환하게됩니다.  아래 그림에서 예시를 볼 수 있습니다.

https://wonit.tistory.com/588 (주의: Java6 까지는 String Pool이 Heap영역에 있지 않다)

이렇게 String만을 위한 특별한 저장소가 존재하는 이유는 단순합니다. 자주쓰이고 많이 쓰이기 때문입니다! 하지만 이렇게 함으로써 동시에 불변(immutable)성질의 장점까지 같이 가져갈 수 있는 것이죠!


주의할 점

위 그림에서도 명시를 했지만, String Constant Pool이 메모리상에서 차지하는 영역은 자바의 버전에 따라 다릅니다. 

  • ~자바6
    • String Constant Pool이 Heap 영역대신 PermGen(Permenent Generation) 영역에 존재했습니다. PermGen은 JVM에서 메소드영역에 있기 때문에 GC가 일어나지 않았고, 사이즈가 32MB~96MB로 고정되어 있었습니다. 따라서 당연하게도 너무 다양한 String을 많이 사용하게 된다면 OOM이 발생하고는 했습니다.
  • 자바 7
    • 위의 이유와 여러가지 이유때문에 String Constant Pool은 자바7부터 PermGen 영역 -> Heap 영역으로 위치를 옮겼습니다. Heap 사이즈에 따라 String Pool도 영향을 받기에 동적으로 메모리크기가 할당될 수 있게 되었습니다. PermGen 영역은 여전히 고정 메모리크기로 할당되었습니다.
  • 자바 8
    • 이때부터 고정크기로 많은 문제를 일으킨 PermGen은 Metaspace로 이름을 변경하고 고정크기에서 동적크기로 메모리 구성이 변경되었습니다.

 

출처:
- [조금 더 깊은 Java] String 과 String Constant Pool
- 자바의 String과 Constant Pool
- 프로그래밍/Java [Java] 자바의 String 클래스의 특별성 간단 정리
- Java – Constant pool과 String pool
- [Java] String Pool에 관하여
728x90