Java에서는 문자열 객체를 생성할 때, String str = "abc"의 리터럴 방식과 String str = new String("") 생성자 방식이 존재한다. 두 방식의 차이를 알아보고자한다.

""String str = "Hello";
permGen이라는 고정 크기의 jvm의 메서드 영역에 존재했다.문자열 상수 풀은 내부적으로 해시 테이블로 구현되어 있다.
문자열 검색 및 저장의 효율성 상승
개념적으로 보면 아래와 같다.(실제는 훨씬 복잡함)
HashMap<String, String> stringPool = new HashMap<>();
// 문자열 리터럴이 사용될 때의 개념적 동작
String internString(String content) {
if (stringPool.containsKey(content)) {
return stringPool.get(content); // 이미 존재하면 기존 참조 반환
} else {
stringPool.put(content, content); // 새로 추가
return content;
}
}
new String("")String str3 = new String("Hello");
String str4 = new String("Hello");
new 키워드로 생성된 문자열 = 객체 -> Heap 메모리따라서 new String("Hello")는 사실상 두 개의 객체를 생성
이는 메모리 효율성 측면에서 매우 떨어진다.
String str1 = "Hello";
String str2 = "Hello";
String str3 = new String("Hello");
String str4 = new String("Hello");
System.out.println(str1 == str2); // true (상수 풀의 동일한 주소 참조)
System.out.println(str1 == str3); // false
System.out.println(str3 == str4); // false
그렇다면 생성자 방식으로 생성된 문자열을 리터럴 방식의 문자열로 변환은 불가능한가?
intern() 메소드를 통해 생성자로 생성된 문자열도 문자열 상수 풀에 추가 가능하다.
String str3 = new String("Hello");
String str5 = str3.intern();
String str1 = "Hello";
System.out.println(str3 == str1); // false
System.out.println(str5 == str1); // true
intern() 메소드는 문자열 상수 풀에서 동일한 내용의 문자열을 찾아 그 참조를 반환하거나, 없으면 해당 문자열을 풀에 추가한다.
동작 과정:
1. String.intern() 호출 시 JVM은 문자열 상수 풀 확인
2-1. 동일한 내용의 문자열이 이미 풀에 존재 -> 문자열의 참조를 반환
2-2. 존재 X -> 호출한 문자열이 풀에 추가되고 그 참조를 반환
문자열 생성시에는 아래를 참고하자:
기본적으로 리터럴("") 사용: 대부분의 경우 리터럴 방식이 메모리 효율, 연산 효율(==), 초기화 시간(매번 객체 생성 X)에서 유리함
필요한 경우에만 new String(): 특별히 새 인스턴스가 필요한 경우에만 생성자 방식 사용, 이때 내용 비교는 equals()로 비교
추가적인 jvm 관련 내용은 다음글에 작성할 예정이다.