java String 리터럴 vs 생성자 차이

Seob·2025년 5월 1일

개념정리

목록 보기
3/4
post-thumbnail

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

1. 문자열 리터럴: ""

String str = "Hello";
  • String Constant Pool 사용: 리터럴로 생성된 문자열은 JVM의 문자열 상수 풀(Heap영역 내부에 존재)에 저장
  • 상수풀에 저장되기 때문에 같은 문자열 value를 가진다면 재사용이 가능하다. -> 메모리 효율 증가

java 버전에 따른 변화

  • java 7 기준으로 이전에는 문자열 상수풀은 permGen이라는 고정 크기의 jvm의 메서드 영역에 존재했다.
  • java 7 이후부터는 문자열 상수풀은 heap 영역으로 이동하여 동적으로 할당되며 GC의 영향을 받게 되었다.
  • 추가적으로 기존의 permGen은 java8부터 삭제되고 Metaspace로 대체됨

문자열 상수 풀의 해시 테이블 구현

문자열 상수 풀은 내부적으로 해시 테이블로 구현되어 있다.
문자열 검색 및 저장의 효율성 상승

개념적으로 보면 아래와 같다.(실제는 훨씬 복잡함)

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;
    }
}

2. 생성자 방식: new String("")

String str3 = new String("Hello");
String str4 = new String("Hello");

특징

  • 힙 메모리 사용: new 키워드로 생성된 문자열 = 객체 -> Heap 메모리
  • 항상 새 인스턴스: 동일한 내용이라도 매번 새로운 객체 생성 -> 메모리 사용 증가

new String()의 내부 동작

  • new String("Hello")와 같은 생성자 방식은 내부적으로 다음 동작 수행
  1. "Hello" 리터럴이 문자열 상수 풀에 추가됨(아직 없는 경우)
  2. 새로운 String 객체가 힙에 생성되고, 내부적으로 동일한 문자 배열을 참조

따라서 new String("Hello")는 사실상 두 개의 객체를 생성

  • 상수 풀에 하나의 문자 데이터
  • 힙에 별도의 String 객체

이는 메모리 효율성 측면에서 매우 떨어진다.

두 방식의 비교

메모리 관점

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

  • 리터럴은 constant pool에 저장되므로 동일한 리터럴이면 동일한 주소 참조
  • 다만 생성자를 통한 문자열 생성 방식은 매번 다른 heap 주소 참조

그렇다면 생성자 방식으로 생성된 문자열을 리터럴 방식의 문자열로 변환은 불가능한가?

3. 인터닝(Interning)

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 관련 내용은 다음글에 작성할 예정이다.

profile
백엔드 개발자 Seob입니다

0개의 댓글