해결된 질문
작성
·
190
1
안녕하세요 선생님~
[JVM이 문자열 상수를 관리하는 구조] 강의를 듣던 중에 이해가 잘 안되는 부분이 있어서 질문드립니다.
제가 기존에 알고 있던 내용과 더불어 강의를 통해 이해한 바로는
1. "런타임 상수 풀"은 문자열 리터럴 객체가 아닌 문자열 리터럴의 심볼릭 참조를 저장하는 곳, 즉 실제 객체는 저장되지 않음 - 물론 여러 다른 심볼릭 참조 또한 저장됨
2. String s1 = "Hello"처럼 리터럴로 선언된 String instance는 Heap 메모리 영역의 "문자열 상수 풀"에 저장
3. String s3 = new String("Hello")는 객체로, Heap 메모리 영역이면서 문자열 상수 풀이 "아닌" 영역에 저장. 즉, 문자열 상수 풀에 독립적
4. 따라서 문자열 상수 선언(String s1 = "Hello") 또는 intern() 메서드 호출할 경우에만 문자열 상수 풀에 문자열 저장이 발생
위의 이해를 전제로 질문은 아래와 같습니다.
[12분 22초]쯤 선생님께서 문자열 상수 풀에서 문자열을 조회할 때,
먼저 Runtime Constant Pool 내에 문자열 리터럴을 조회하고, 있으면 바로 반환한다고 하셨는데요.
1. 런타임 상수 풀에서만 리터럴을 조회하고 끝나는 절차가 맞는지요? 혹은 "런타임 상수 풀"에 저장된 문자열 리터럴의 심볼릭 참조를 타고 "문자열 상수 풀"의 문자 리터럴 객체를 탐색하는 것까지가 맞는 처리 과정인지요?
-> 저는 후자가 맞다고 생각하고 있습니다.
2. intern() 호출 시, 런타임 상수 풀 내의 "찾고자 하는 문자열 리터럴의 심볼릭 참조"가 존재하지 않는다면, JVM이 문자열 객체를 생성하여 Heap의 문자열 상수 풀에 저장하나요? 혹은 Heap 영역이지만 문자열 상수 풀이 아닌 영역에 저장되나요?
-> 저는 intern() 호출로 생성된 객체는 항상 문자열 상수 풀에 저장되는 걸로 알고 있습니다.
3. 그리고 2번 질문과 연관된 질문인데, 수업자료의 예제에서
public class Main {
public static void main(String[] args) {
String s1 = "Hello";
String s2 = "Hello";
System.out.println(s1 == s2);
String s3 = new String("World");
String s4 = s3.intern();
System.out.println(s3 == s4);
System.out.println("World" == s3); // 1번
System.out.println("World" == s4); // 2번
}
}
18분 51초 쯤 선생님께서 말씀하시기를 s3.intern()으로 인해 "World" 리터럴이 Runtime constant pool에 생긴다고 하셨는데, Runtime constant Pool은 오직 심볼릭 참조만 가지고 있는거 아닌가요?
Runtime constant pool는 클래스 로드 시 정적으로 생성된 심볼릭 참조를 관리할 뿐이고, 동적으로 업데이트 되는건 아니지 않나요?
따라서 s3.intern()으로 인해 "World" 리터럴은 Runtime Constant Pool이 아니라 Heap 영역의 문자열 상수 풀에 들어가야하는 것이 아닌지요?
마지막으로,
[System.out.println("World" == s3); // 1번]
여기서 false가 나온 이유는 new로 동적 할당된 "World"은 문자열 상수 풀이 아닌 Heap 영역에 저장되기 때문에
s3 참조자는 문자열 상수 풀이 아닌 그 외 Heap 영역의 String instance를 바라보고 있고,
s4 참조자는 intern() 함수로 인한 호출로 문자열 상수 풀에 생긴 객체이기 때문에 false가 나온게 아닌지요?
제가 잘못 알고 있는 상태에서 잘못된 질문을 길게 한건가 싶어서 죄송스럽네요.
늘 좋은 강의 감사합니다!
답변 1
1
Runtime constant pool은 심볼릭 참조 정보와 문자열 상수가 저장되는 공간입니다. 일반 문자열이든 형식 문자열이든 리터럴로 기술한 문자열 정보가 모두 담깁니다. 여기서 중요하게 볼 부분은 이 문자열 정보를 String 클래스의 인스턴스로 볼 것인가 입니다. 결론 부터 말씀드리면 그렇지 않다고 보는 것이 적절하다는 것입니다. 명세 수준에서 런타임의 문자열은 모두 String 클래스의 인스턴스라고 기술하고 있을 뿐입니다.
그런 관점에서 최초 Runtime constant pool에 저장된 문자열 정보를 String 클래스 인스턴스로 이해했었습니다. 그러나 제가 생각을 바꾼 이유는
1. 아무리 명세에 관련 구분이 없다 하더라도 같은 문자열 정보가 두 개의 인스턴스로 존재할 이유가 없고
2. 관리체계는 하나로 통합하는 것이 적절하다
라고 판단했기 때문입니다.
3. 아울러 클래스가 로딩(링킹 포함)과정에는 인스턴스 생성이 포함되지 않으므로 문자열 정보가 메모리에 로드되었다 하여 이를 String 클래스 인스턴스로 보는 것은 부적절하다고 판단했습니다.
public class Main {
public static void main(String[] args) {
String s1 = new String("Hello");
String s2 = "Hello";
System.out.println(s1.intern() == s2);
}
}
//실행결과
true
그러므로 제가 강의에서 'Runtime constant pool에서 문자열을 조회한다'라고 표현한 것은 잘못된 내용이며 Heap영역에 속한 String (constant) pool에서 검색한다라고 수정하는 것이 적절하겠습니다.
다만, 질문에서 '찾고자 하는 문자열 리터럴의 심볼릭 참조 존재 여부'를 논하는 것은 바르다 할 수 없겠습니다. intern() 메서드는 에초에 참조를 찾을 이유가 없고 String (constant) pool을 검색하면 되기 때문입니다.
결론을 다시 요약하면 문제의 핵심은 클래스 로딩 중 문자열 정보를 String 클래스의 인스턴스로 볼 것인가에 관한 문제로 발생한 저의 착오라 하겠습니다. 사안이 중대하다고 판단해 답변을 달아두기 전에 강의자료 및 영상 업데이트부터 진행했습니다.
감사의 뜻으로 Part 3를 무료로 수강하실 수 있도록 조치하겠습니다. cx8537@naver.com으로 인프런 가입 메일 계정정보를 보내주시기 바랍니다. 감사합니다. 😄
저도 파트3 무료로 수강할수있게 해주실수없는지요?