우연히 String에 대해서 비교 연산자(==)를 사용했는데, true가 나와서 적지 않게 당황했다😱
public class StringConstantPool {
public static void main(String[] args) {
String literalAppleOne = "Apple";
String literalAppleTwo = "Apple";
String newAppleOne = new String("Apple");
String newAppleTwo = new String("Apple");
// literalAppleOne == literalAppleTwo: true
System.out.println("literalAppleOne == literalAppleTwo: " + (literalAppleOne == literalAppleTwo));
// literalAppleOne == newAppleOne: false
System.out.println("literalAppleOne == newAppleOne: " + (literalAppleOne == newAppleOne));
// newAppleOne == newAppleTwo: false
System.out.println("newAppleOne == newAppleTwo: " + (newAppleOne == newAppleTwo));
}
}
String은 Reference Type
이므로 비교 연산을 했을 때, 주소 값으로 비교를 하다보니, 당연히 false가 나올 거라고 예상했다.
new 연산자로 생성한 String 객체는 예상과 같이 false가 나왔는데, literal로 선언한 String은 예상과 달리 주소값이 일치했다. 주소값이 어떻게 같지..?! 상단 코드의 출력 결과에 대해서 파악하기 위해 String 생성시에 내부적으로 어떻게 동작하는지 공부해 보았다!
String newAppleOne = new String("Apple");
String newAppleTwo = new String("Apple");
Reference Type의 객체들은 생성시에 Heap영역에 할당된다.
마찬가지로 new 연사자를 통해 문자열 객체 생성 시, 같은 문자열이더라도 Heap영역에 따로 생성된다. newAppleOne과 newAppleTwo의 참조값은 다르게 생성되므로, 결과적으로 false가 나온다.
String literalAppleOne = "Apple";
String literalAppleTwo = "Apple";
JVM은 literal을 통해 문자열 객체를 생성할 경우 String Constant Pool
이라는 특수한 저장 공간에서 동일한 문자열이 있는지 찾는다. 존재하면 재사용을 하고, 없는 경우 생성 해준다.
(그래서 literal을 통해 선언된 문자열의 참조값이 같던 거였군😎)
literal로 생선된 문자열에 대해서 캐시 역할을 해준다고 생각이 들었다.
그렇다면, String Constant Pool이 무엇인지 좀 더 알아보자.
String Constant Pool
)이며, 문자열 리터럴을 저장하는 용도로 사용된다. HashMap
으로 구현이 되어 있으며, key값은 hashCode()를 사용한다. 메모리 부하를 줄이기 위해서
String 객체를 생성하기 전에 String Constant Pool에서 해당 문자열이 있는지 체크한 후에 존재 하지 않을 경우 생성하고, 존재할 경우에는 기존 문자열을 참조한다.내가 처음에 원했던 질문에 대한 답을 알게 되었다. Reference Type에 등가 비교 연산(==)을 할 경우, 역시나 주소로 비교한다. 그렇지만 Referece Type인 String을 literal 객체로 생성했을 때, 동일한 주소라고 나왔던 이유는 Heap영역에 있는 String Constant Pool에서 literal String을 관리하며, 기존에 존재하는 문자열인지 체크 후에, 재사용 하기 때문이다.😌
https://www.baeldung.com/java-string-constant-pool-heap-stack
정말 유익한 글이었습니다.