2021. 5. 22. 00:51ㆍJava/Etc
우리는 String 하면 문자열을 표현하는 데이터 타입(DataType)의 하나로 알고 간단하게 쓰고 있지만 String Class에는 다른 데이터 타입(Primitive Data Type)과는 다른 몇 가지의 특별한(?) 특징이 존재한다.
오늘은 그 각각의 특징에 대해 정리해 보려 한다.
String Class 선언 및 초기화
데이터 타입의 선언은 보통 해당 데이터 타입의 변수를 지정한 뒤 값 리터럴( literal )을 입력하는 방식으로 이뤄진다.
String 역시 다른 데이터 타입과 동일하게 선언과 초기화할 수 있으며 해당 방법으로 많이 사용되고 있다.
//각 데이터 자료형의 변수 선언과 동일하게 사용가능
int intVal = 0;
char charVal = 'a';
String strVal = "문자열";
우리는 단순히 다른 데이터 타입의 변수와 동일하게 위 방식으로 사용하고 있는데 여기에서 String의 특징이 숨어있다.
String의 값은 힙(Heap) 메모리 영역에 저장된다.
보통 데이터 타입의 변수들을 선언하고 값(literal)을 대입하게 되면 해당 값 자체를 저장하는 메모리의 스택(Stack) 영역에 저장되게 되는데, String 변수 역시 동일하게 스택(Stack) 영역에 선언되지만 값 자체가 아닌 참조 값이 저장되고 실제 값은 힙(Heap) 메모리 영역에 저장된다.
이 때문에 String은 참조값을 가지는 참조 자료형 변수(Reference Data Type)로도 선언 및 초기화가 가능한 것이다.
String의 아래 두 가지 선언 방식 모두 결과적으로는 변수에 힙(Heap) 영역의 참조 값을 저장하는 방식인 것이다.
//변수의 값은 참조값으로 스택(Stack)에 실제 입력값은 힙(Heap)메모리에 저장
String literal = "About String";
String reference = new String("About String");
그럼 왜 String만 별도로 Heap 영역에 값을 저장하도록 설계된 것일까??
우리가 프로그램을 함에 있어 String 은 그 어떤 데이터 유형보다 많이 사용한다고 본다.
사용량은 결국 메모리를 많이 사용하고 그렇기 때문에 특별한 메모리 관리 부분이 존재해야 하는 이유이기도 하며,
String만의 별도의 Heap 영역에 관리를 함으로써 '메모리를 효율적으로 관리'하기 위한 방법인 것이다.
이제 String의 값은 Heap 영역에 저장되는 것은 알았으니 그럼 Heap에서 어떤 식으로 관리되는지 확인해 보자!~
String 은 String Pool 이 존재한다.
String Class는 불변(immutable)의 특성을 가지고 있고 이는 '동일한 (literal ) 값의 문자열에 대해서는 단 하나의 객체만을 생성한다.'라는 전제하에 설계되었다.
불변 객체(Immutable Object)
우리는 급변하는 세상 속에서 살고 있지만 그중에서도 절대 변하지 않았으면 하는 것은 있다. 많은 변화 속에 변하지 않는 그것 불변(Immutable)의 특징에 대해 알아보자. 불변 객체(Immutable Object)
lovelettee01.tistory.com
단 하나의 객체라는 것은 결국 매번 변수를 선언할 때마다 Heap 영역의 새로운 공간에 객체를 생성하게 되면 메모리가 비효율적이기 때문에 이미 생성된 문자열 객체에 대해서는 문자열 캐싱(Caching)을 통해 참조 값을 리턴해 주게 되는 것.
이렇게 문자열 캐싱(Caching) 해놓은 곳이 바로 String Pool이라는 영역이 되는 것인데, 그렇다면 모든 String은 위에서 말한 동일한 값(literal)의 문자열에 대해서는 모두 하나의 객체만을 생성하는 String Pool에 존재하게 되는 것일까?
String literal = "StringPoolTest";
String reference = new String("StringPoolTest");
System.out.println(literal == "StringPoolTest"); //true - String Pool (O)
System.out.println(literal == reference); //false - String Pool (X)
실제 값(literal)을 통해 선언한 방식만이 서로 같은 참조 값을 가지게 되고 같은 문자열임에도 new Object를 통한 참조형(reference type) 초기화 방식은 다른 참조 값을 가진다는 것을 확인할 수 있다.
즉, literal을 통해 선언 및 초기화를 한 경우 단 하나의 참조 값을 가지는 String Pool에 등록되어 관리되고, new Object를 이용한 초기화는 Heap 영역에 등록은 되지만 서로 다른 참조 값을 가지는 String Pool 영역에 존재하지 않는다는 것을 확인할 수 있다.
이처럼 literal 방식의 선언을 하면 내부적으로 intern() 메서드를 통해 String Pool이 관리되는데 intern() 메서드에 대해서 알아보자.
String의 intern() 메서드 통한 String Pool 관리
- HashTable로 이루어진 String 문자열 관리
- String Pool에 캐싱된 문자열 확인 후 문자열이 존재할 경우 해당 객체를 리턴
- 캐싱된 문자열이 존재하지 않을 경우 새로 등록 후 등록된 객체를 리턴
- intern()을 통해 참조 값이 동일한 항목에 대해서는 equals() 또한 동일한 결과를 리턴한다.
- java runtime 시에 -xx:StringTableSize 옵션을 통해 Size를 지정할 수 있다.
※ Java 버전별로 서로 다른 String pool 관리가 이뤄지고 있는데 이건 해당 사이트를 참고하면 좋다.
String.intern in Java 6, 7 and 8 - string pooling - Java Performance Tuning Guide
This article will describe how String.intern method was implemented in Java 6 and what changes were made in it in Java 7 and Java 8.
java-performance.info
참고로 new Object를 통한 String 객체를 생성한 경우 Heap영역에 별도로 생성되는 참조값을 String Pool에 등록하기 위해서는 임의적으로 intern() 메서드를 호출하면 String Pool로 참조값이 변경된다.
String literal = "StringPoolTest";
String reference = new String("StringPoolTest");
System.out.println(literal == reference); //false - 서로다른 참조값을 가짐
System.out.println(System.identityHashCode(literal)); //93122545
System.out.println(System.identityHashCode(reference)); //2083562754
reference = reference.intern(); //String Pool로 등록
System.out.println(System.identityHashCode(reference)); //93122545
System.out.println(literal == reference); //true - String Pool참조값
각 방식대로 String을 선언해서 사용할 때 효율(시간)은 literal > reference > reference.intern() 순으로 이루어지기 때문에 값(literal)을 이용해 선언하는 것이 제일 좋다.
String Class는 " "(literal)을 이용한 선언 및 초기화가 👍 이다.
'Java > Etc' 카테고리의 다른 글
문자열(String) Class 사용법 (0) | 2021.08.09 |
---|---|
불변 객체(Immutable Object) (0) | 2021.06.04 |
프로그램에서 음수 표현 방식 (0) | 2021.05.14 |