[Java] String 생성 방식! 리터럴방식(상수풀) vs new 연산자 방식

devdo·2022년 8월 26일
0

Java

목록 보기
49/56

String의 불변성 (Immutable)

자바에서 String은 불변 객체(immutable)라고 한다. 즉, 객체 생성 이후 내부의 상태가 변하지 않는 객체라는 뜻이다.

그러면, String 객체는 최초 한 번 생성되면 절대로 그 값이 변하지 않는다는데, 이런 상황은 어떻게 설명할 수 있을까요?

String str = "최초 문자열";
str = "변경된 문자열";

str 이라는 String 객체가 생성된 이후 "최초 문자열""변경된 문자열" 로 바꾼다고 해도 실제 내부적으로는 최초 생성된 String 객체의 값이 변경되는 것이 아니라 새로운 String 객체가 생성되어 그 참조가 str 변수에 할당된 것입니다.

즉, 이 상태에서는 최초에 생성된 "최초 문자열" 과 "변경된 문자열" 두 개의 인스턴스가 Heap 영역에 저장되어 있는 상태입니다.
(이후 참조되지 않는 "최초 문자열"은 GC(Gabage Collection)을 통해 제거됩니다.)

💥 혼동하지 말아야할 개념

불변성의 개념, 문자열을 재할당할 때 해당 문자열을 참조하는 객체를 수정하는 것이 아니라 새 문자열을 만들고, 변경된 문자열을 할당한다는 것.
(즉, 문자열이 변경 불가능하다는 것은 객체 자체는 변경할 수 없지만 객체에 대한 참조는 변경할 수 있다.)


String을 불변 객체로 사용하는 이유

그러면 자바에서는 왜 String을 불변 객체로 만들었을까?

 String str1 = "madplay";
 String str2 = "madplay";

 System.out.println("str1 == str2 : " + (str1 == str1));  // true

위의 코드 실행 과정을 분석해보면 문자열 s1에 해당하는 것을 상수 풀에서 찾는다. 없다면 상수 풀에 등록하고 해당하는 레퍼런스 값을 반환한다.

문자열 리터럴을 캐싱하고 재사용하면 문자열 풀의 다른 문자열 변수가 동일한 개체를 참조하기 때문에 힙 공간을 많이 절약할 수 있습니다. 문자열 상수 풀은 이러한 장점을 가지고 있는 것이다.

String이 불변해야 하는 더 자세한 이유는 이 블로그를 참고하기 바란다.
https://starkying.tistory.com/entry/why-java-string-is-immutable


자바에서는 String을 선언하는 방법

그런데, 자바에서는 String을 선언하는 방법이 두 가지가 있다. new 연산자 방식, 리터럴 방식이다. 이 둘은 어떤 차이가 있을까?

 String str1 = new String("madplay"); // new 연산자 방식
 String str2 = "madplay";			// 리터럴 방식

 System.out.println("str1 == str2 : " + (str1 == str1));  // false

이 둘의 차이는 실제 메모리에 할당되는 영역에 차이가 있다!

new 연산자를 통해 문자열 객체를 생성하는 경우, 메모리의 Heap 영역에 할당되고 두 번째 방법인 리터럴 방식으로 생성할 경우에는 String Constant Pool이라는 상수풀 영역에 할당된다.

참고로, 문자열이 담기는 상수풀의 위치는 자바 7부터 Heap영역으로 옮겨졌다.

그래서 Heap 영역에서 할당하는 메모리 번지가 다르기 때문에 주소값이 달라 false 가 나오는 것이다.

메모리 할당한 영역이 달라지니 주소값비교(==)를 하면 false가 나오는 것이다!

public class MadPlay {
    public static void main(String[] args) {
        String str1 = "madplay";
        String str3 = new String("madplay");
    
        System.out.println(str1 == str3); // print 'false'
    }
}

참고로, 값만 비교하는 eqauls()로는 true가 나올 수 있다. 헷갈리지 말자!

System.out.println(str1.equals(str3)); // print 'true'

String Constant Pool

상수풀(String Constant Pool)의 위치는 Java 7부터 Perm 영역에서 Heap 영역으로 옮겨졌다. Perm(정확히 풀어서 써보면 Permanent Generation)영역은 실행 시간(Runtime)에 가변적으로 변경할 수 없는 고정된 사이즈이기 때문에 intern 메서드의 호출은 저장할 공간이 부족하게 만들 수 있었다. 즉 OOM(Out Of Memory) 문제가 발생할 수 있는 위험이 있었던 것이다.

Heap 영역으로 변경된 이후에는 상수풀에 들어간 문자열도 Garbage Collection 대상이 된다.

관련 링크) JDK-6962931 : move interned strings out of the perm gen(Oracle Java Bug Database)

이후에 Java 8 버전에서는 Perm 영역은 완전히 사라지고 이를 MetaSpace라는 영역이 대신하고 있다.



참고

profile
배운 것을 기록합니다.

0개의 댓글