Inor

[Java] String (String constant pool) 본문

Computer Engineering/Java

[Java] String (String constant pool)

Inor 2017. 11. 27. 16:53

- String


 String은 문자열을 나타내는 클래스 입니다. 자바에서 가장 자주 사용되는 클래스 중 하나 입니다. 그러나 String 클래스의 특징을 제대로 이해하지 못하고 사용하는 사람들이 많다고 생각합니다. JVM이 메모리 영역에 String 객체를 할당하는 방법을 보면 String 객체가 갖고있는 특성을 이해할 수 있습니다.




- String 객체 생성


 String 클래스를 이용해서 String 객체를 생성하는 방법이 몇 가지 있는데, 각 방법을 살펴보겠습니다. 아래와 같은 코드가 있고 "StringA" 문자열을 만드는 방법들입니다. 다음과 String을 6번 만드는 시도를 했을때 과연 JVM에서 몇개의 객체를 Heap 영역에 생성해주는지 생각해 보시길 바랍니다. 아래 출력 코드는 각 참조객체 Hash값을 출력하는 부분입니다. 주소값은 아니지만 이해의 편의를 위해서 Hash값을 주소값이라 표현하겠습니다.


public class StringStudy {

	public static void main(String[] args) {
		String s1 = new String("StringA");
		String s1_1 = new String("StringA");

		String s2 = "StringA";
		String s2_1 = "StringA";
		
		System.out.println("s1   : " + System.identityHashCode(s1));
		System.out.println("s1_1 : " + System.identityHashCode(s1_1));
		System.out.println();

		System.out.println("s2   : " + System.identityHashCode(s2));
		System.out.println("s2_1 : " + System.identityHashCode(s2_1));
	}
}


 아래의 결과는 Heap 영역에 몇개의 객체가 탄생했는지 나타냅니다. 총 6개의 결과 중에 주소값이 중복되는 4가지 경우가 있습니다. 중복을 제외하면 총 3개의 객체가 Heap 영역에 탄생했다는 사실을 알수있습니다. 이런 일이 발생하는 이유는 new 연산자를 사용하면 JVM에서 String 객체를 다른 객체와 마찬가지로 Heap 영역에 새로 생성하기 때문입니다. 그렇기 때문에 s1과 s1_1이 다른 객체로 생성됩니다.

 s2와 s2_1 주소값이 같은 이유는 JVM에서 String을 조금 특별하기 관리하기 때문입니다. new 연산자가 아닌 리터럴("")을 이용해서 String 객체를 생성하면 JVM은 먼저 String constant pool 영역을 찾아갑니다. String constant pool 영역은 이후에 본문에서 조금 더 자세히 다루겠습니다. JVM은 String constant pool 영역에서 이전에 같은 값을 갖고있는 String 객체가 있는지 찾습니다. 만약, 같은 값을 갖고있는 String 객체가 존재 하면 해당 객체의 주소값을 반환하여 참조하도록 합니다. s2와 s2_1 주소값이 같은 이유는 String constant pool의 객체를 참조하고 있기 때문입니다.






- String.intern()


 String에는 intern()이라는 메서드가 존재합니다. 이 메서드는 호출하는 String 객체의 문자열 값이 String constant pool에 있는지 찾아보고 있으면 해당 주소값을 반환합니다. 리터럴("")을 이용해서 String 객체를 생성하면 내부적으로 intern() 메소드가 호출됩니다. 문자열 값을 갖고있는 객체가 없을 경우에는 String constant pool에 객체를 생성하고, 그 주소값을 반환합니다. 아래는 intern()을 사용했을때 결과를 출력하는 코드입니다.


public class StringStudy {

	public static void main(String[] args) {
		String s1 = new String("StringA");
		String s1_1 = new String("StringA");
		
		String s2 = "StringA";
		String s2_1 = "StringA";
		String s2_2 = s1.intern();
		
		String s3 = new String("StringB");
		String s3_1 = s3.intern();
		String s3_2 = s3.intern();
		
		System.out.println("s1   : " + System.identityHashCode(s1));
		System.out.println("s1_1 : " + System.identityHashCode(s1_1));
		System.out.println();
		
		System.out.println("s2   : " + System.identityHashCode(s2));
		System.out.println("s2_1 : " + System.identityHashCode(s2_1));
		System.out.println("s2_2 : " + System.identityHashCode(s2_2));
		System.out.println();
		
		System.out.println("s3   : " + System.identityHashCode(s3));
		System.out.println("s3_1 : " + System.identityHashCode(s3_1));
		System.out.println("s3_2 : " + System.identityHashCode(s3_2));
	}
}


 intern()은 String constant pool에 해당 문자열을 찾아서 주소값을 반환하는 메소드입니다. s2_2의 결과를 확인해보면 s2가 이미 StringA라는 문자열 객체를 String cosntant pool에 만들었기 때문에, s2_2는 s2 객체 주소값을 참조합니다. s1, s1_1과 주소값이 다른 이유는 s1, s1_1은 String constant pool에 존재하는 객체를 참조하는 경우가 아니고 Heap 영역에 존재하는 별도의 객체를 참조하기 때문입니다.

 s3_1은 s3가 갖고있는 문자열 값인 "StringB"를 String constant pool에서 찾지만 없는 것을 확인합니다. JVM이 String constant pool에 "StringB"를 생성하면 s3_1은 주소값을 참조합니다. s3가 생성한 "StringB" 객체는 String constant pool에 존재하는 객체가 아니고 Heap 영역에 별도로 존재하는 객체입니다. s3_2도 "StringB" 값을 갖고있는 객체를 String constant pool에서 찾습니다. s3_1이 String constant pool에 생성한 "StringB" 객체가 있기 때문에 s3_2는 주소값만 참조합니다.





- String constant pool



 String constant pool 영역은 Heap 영역에서 String 객체를 위해 별도로 관리하는 장소입니다. String constant pool은 Heap 영역 내부에 존재합니다. 위 그림을 잘 확인하면 참조 변수인 s1, s2, s3가 있습니다. 여기서 s1,s2는 위 코드의 s1, s2와 다른 참조 변수입니다. s1과 s2는 String constant pool 내부에 있는 "java" 객체를 참조하고 있습니다. s3는 String constant pool 외부에 있는 "java" 객체를 참조하고 있습니다. 위 그림으로 유추해보면 s1과 s2 주소값은 같고 s1과 s3 주소값은 다를 것입니다. 그리고 s1과 s2는 리터럴("")을 사용해서 생성된 String 객체이고 s3는 new 연산자를 이용해서 생성된 객체일 것입니다. s1과 s2 중에서 어떤 객체가 먼저 참조를 시작했는지 모르지만 리터럴("")연산자를 이용해서 한 객체가 먼저 "java"를 String constant pool에 생성 했을 것입니다. 생성 이후에 다른 객체가 리터럴("")을 이용해서 "java"를 생성하려 했고 JVM은 String constant pool을 확인해서 "java"객체의 주소값을 반환 했을 것입니다.

 java7 이전 버전에서 String constant pool은 Perm 영역에 존재하지만 String constant pool영역은 java7 이후 부터 Heap 영역에 존재합니다. 그리고 이 영역에 존재하는 String 객체들은 Heap 영역에 존재하기 때문에 GC의 대상이 됩니다.

Comments