목표
- String이 Heap 메모리 상에서 어떻게 저장되는지 확인한다.
개요
JVM에서 변수를 저장하게 선언하면 해당 변수는 Heap Area에 저장된다(그림1 참조). String도 Heap Area에 저장되는 것은 마찬가지인데 String은 많이 쓰이는 변수인 만큼 조금 특이한 방식으로 저장된다.
*이것에 익숙하지 않다면 먼저 JVM의 메모리 사용 방식을 보고 오자
String은 우리가 가장 많이 쓰는 클래스 중 하나이다. String을 효율적으로 사용하기 위해 JVM상에서 String을 다른 객체들과 차별되게 저장되도록 해놓았는데, 그것이 바로 Heap Area 상의 String Constant Pool이다.
예를 들어 다음과 같은 코드가 실행된다고 해보자
val stringA1 = "A"
val stringA2 = "A"
val stringKotlin = "Kotlin World"
위 변수들은 Heap Area에 다음과 같이 저장된다.
String Constant Pool은 플라이웨이트 패턴을 구현한 대표적인 예로 한 번 저장한 변수를 다시 저장하지 않도록 만들어졌다. 따라서 String을 ""을 이용하여 선언할 경우 먼저 String Constant Pool에 해당 변수가 있는지 확인 후 있으면 기존 값을 참조하도록 주소값을 설정하고, 아니라면 새로운 String은 String Constant Pool에 넣는다. 이러한 과정을 통해 String은 JVM 상에서 매우 효율적으로 동작할 수 있다.
그렇다면, 다른 변수들 처럼 String Constant Pool 바깥에 String을 저장할 수 있는 방법은 없을까? 당연히 있다. ""가 아니라 String 생성자를 이용하여 String을 생성할 경우 Constant Pool 바깥에 생성된다.
val stringABC1 = String(charArrayOf('A','B','C'))
이렇게 될 경우 더이상 String Constant Pool에 String이 없으므로 같은 String을 생성하더라도 참조되지 않는다.
val stringABC1 = String(charArrayOf('A','B','C'))
val stringABC2 = "ABC"
예시
Kotlin을 이용해 위를 확인하는 방법은 간단하다. ==와 ===을 이용해 알아보는 것이다. ==는 같은 hashCode 값을 같고 있는지 비교하는 것이고, ===는 같은 주소값을 가지고 있는지 비교하는 것이다. (Java와 다르니 혼동하지 말자)
val stringA1 = "A"
val stringA2 = "A"
stringA1 == stringA2 // true
stringA1 === stringA2 // true
val stringABC1 = String(charArrayOf('A','B','C'))
val stringABC2 = "ABC"
stringABC1 == stringABC2 // true
stringABC1 === stringABC2 // false
위를 보면 ==는 equals연산이 수행되어 hashCode() 값을 비교하여 같은 값을 가질 경우 true를 반환하며 ===는 같은 주소값을 가지는지 여부이므로 stringA1과 stringA2만 true를 반환하고, stringABC1,stringABC2는 false를 반환한다.