Closed glenn-syj closed 7 months ago
Glenn-syj님의 String pool에 대한 상세한 보충 설명 감사드립니다.
막연하게, String pool 에 등록되어있는 값을 다른 String 에서 갖게 될 때, 두 String은 같은 참조값을 갖게 된다고만 인식하였는데, 리터럴과 생성자로 String 객체를 다루었을 때의 차이점을 세 가지 예제 코드로 설명해주셔서 원리를 제대로 이해할 수 있게 되었습니다.
생성자를 통해 String 객체를 생성할 경우, 기존의 String pool 내부에 같은 문자열에 대한 공간과 데이터가 존재한다고 하더라도, 새로운 메모리 공간이 힙에 할당되어 새로운 참조값을 가지게 되는 것을 알게 되었습니다.
String b = "헬로";
System.out.println(b.hashCode()); // 1744880
b = "안녕";
System.out.println(b.hashCode()); // 1611021
b += "하세요";
System.out.println(b.hashCode()); // 803356551
여기서 b += "하세요"는 비록 리터럴끼리의 연산이지만, 기존의 String pool에 "안녕하세요" 가 존재하지 않기 때문에, String pool 내부에 새로운 공간을 할당하여 "안녕하세요" 가 저장되기 때문에 다른 참조값을 나타내게 되는 것입니다.
이러한 String 클래스의 동작원리를 가능케하는 intern() 메서드를 추가로 알게되어, String과 String pool에 대한 내부적인 작업이 이루어질 때, 그 과정을 머릿속에 그릴 수 있게 되었습니다.
based on: #81 by @undeadtimo
들어가며
String
자료구조에서 드러나는 불변성을 잘 지적해주셨는데요. JVM과 관련된 부분이기도 해, 이번 글에서는 String Pool에 대해 부가적인 설명을 하려고 합니다. 물론 String Pool의 이용이 불변성에서 비롯되는 것이기도 하고요.String Pool이란
스트링 풀은 JVM 힙 메모리에 저장되는 String의 묶음이라고 볼 수 있습니다. String 객체를 생성하고 값을 할당하면, JVM이 그와 같은 값에 대한 String을 찾는데요. 만약 String Pool 내에서 해당 값이 발견된다면, 자바 컴파일러는 메모리에 대한 참조값만 반환합니다.
자바 7 이전에는 JVM에서 고정 크기를 가진 PermGen 공간에 String Pool을 두었습니다. 그래서 런타임 중에 메모리 공간이 확장될 수 없어, 에러가 나기도 했다고 합니다. 물론 자바 7 이후에는 힙 공간에 두었을 뿐만 아니라, 참조되지 않은 String을 가비지 콜렉팅할 수도 있게 되었습니다.
String Pool 심화
그러나 String Pool은 단순히 동등한(#90) 객체를 생성할 때마다 이용되는 것은 아닙니다. 아래 명시한 참고 자료에서 이해에 도움이 되는 코드가 있어 가져왔습니다.
1번 코드는 두 String 객체 모두 리터럴을 이용해서 생성되었습니다. 2번 코드에서는
new
키워드와 함께 생성자가 이용되었구요. 3번 코드에서는 각기 따로 이용되었습니다. 위 1번~3번 코드에서 주석으로 처리된 결과에서 String 객체 생성 시에 String Pool이 이용되는 지 여부를 잘 드러냅니다.즉, 리터럴을 이용한 생성에서는 String Pool에 있는 값을 재사용하는 반면,
new
키워드와 생성자를 이용할 때에는 힙 메모리에 새로운 객체가 생성됩니다. 따라서 상황이 허락하는 한에서는 리터럴을 이용한 생성이 더욱 효율적이라는 결론으로도 이어집니다.String Pool 조작
intern()
메소드를 이용하면 String Pool을 수동적으로 조작할 수 있습니다. 아래는 자바8 공식문서에서의 설명입니다.즉,
intern()
메소드는 String Pool에 해당 문자열이 (1) 등록된 경우와 (2) 등록되지 않은 경우에 다르게 동작합니다. (1) 등록된 경우에서는intern()
메소드가 풀 내에 있는 String을 반환합니다. (2) 등록되지 않은 경우에 대해서는 String Pool에 등록한 뒤, 참조값을 반환합니다. 당연하게도, 이 과정에서는 동등성이 이용됩니다.또한, 위 문서에 모든 리터럴을 이용한 String이 위 메소드와 같은 방식으로 동작한다고 명시 되어 있다는 점도 흥미롭습니다.
References
https://docs.oracle.com/javase/8/docs/api/java/lang/String.html https://www.baeldung.com/string/intern https://www.baeldung.com/java-string-pool