[Java] String

Future·2024년 8월 26일
1

java

목록 보기
13/13

intro

프로젝트를 하면서 String에서 '+' 를 사용할 일이 많았다.
String은 불변 객체라 +를 사용하면 내부적으로 new 를 사용해 객체를 계속 생성하여 성능에 좋지 않다는 이야기를 들은 적이 있는 것 같아서 한번 찾아보고 정리해보았다.

String의 참조

String은 참조형 데이터 타입이다.
Stack 영역에 데이터가 저장되는게 아니라, Stack 영역에서 Heap 영역의 값을 참조하여 사용한다.

String name = "홍길동"

이렇게 선언하면, Heap의 String Constant Pool에 문자열 "홍길동" 이 저장되고, 스택 영역의 name에는 "홍길동"의 주소값이 저장된다. (C언어 포인터마냥..)

이때 Stack의 참조 변수의 크기는 위와 같다고 한다.

String은 선언 방법에 따라 Heap의 String Constant Pool 내부/외부에 저장된다.
정확히 말하면, Heap의 String Constant Pool 내부/외부의 값을 참조한다.

str1과 str2를 아래와 같이 문자열 리터럴로 하드코딩하면 String Constant Pool에 저장된다.

String str1 = "Hello";
String str2 = "Hello";

str1과 str2는 결국 주소를 참조한다. (str1과 str2의 hashCode 값)

String str1 = "Hello";
String str2 = "Hello";
String str3 = new String("Hello");

System.out.println(str1 == str2);           // true
System.out.println(str1 == str3);           // false
System.out.println(str1.equals(str3));      // true


위 코드는 해당 그림과 같이 저장된다.
str1과 str2는 String Constant Pool에서 같은 객체를 참조하고 str3는 힙에 새로 객체를 생성한다.

String Constant Pool 덕분에 동일한 문자열 리터럴에 대해 새로 메모리를 할당하지 않고, 같은 객체의 주소를 반환함으로써 메모리를 효율적으로 사용할 수 있다. 힙에서 메모리를 새로 할당하지 않는다는 것은 GC의 일을 줄여준다는 말이기도 하다.
그리고 이는 String Constant Pool의 문자열 리터럴이 immutable(불변)하기 때문에 가능하다.
또한 String Constant Pool은 내부적으로 HashTable 구조로 설계되어 특정 문자열이 존재하는지 여부를 빠르게 찾을 수 있다.

String Constant Pool은 Java8 이후부터는 사이즈를 조정할 수 있다.

String의 + 연산

String의 + 연산 시, 객체를 마구 생성한다는 말은 JDK 1.5 이전의 일이라고 한다.
내 생각에는

String str = "a" + "b" + "c";

위와 같이 선언하면 String Constant Pool에 a, b, c가 각각 따로 저장된다고 생각했다.
이 동작은 jdk 1.5 이전의 동작이고, 현재는 컴파일 시에 StringBuilder를 사용하도록 최적화 되어있다.

String str = "a" + "b" + "c";

이 경우에는 컴파일러가 StringBuilder를 사용하지 않고 아래와 같이 최적화한다.

String str = "abc";
String str = "";
    str += "a";
    str += "b";
    str += "c";
String str = "";
for (int i = 0; i < 3; i++) {
    str += i;
}

위와 같이 여러 줄에 걸쳐 + 연산을 하는 경우나
반복문을 통하여 + 연산을 하는 경우에는 아래와 같이 StringBuilder 객체를 new 로 계속 생성한다.

String str = "";
    str = (new StringBuilder()).append(s1).append("a").toString();
    str = (new StringBuilder()).append(s1).append("b").toString();
    str = (new StringBuilder()).append(s1).append("c").toString();
String str = "";
for(int i = 0; i < 3; i++) {
    str = (new StringBuilder()).append(s2).append(i).toString();
}

결론은 그냥 간단한 문자열 결합은 한 줄에서 +를 사용하는 것이 좋고,
반복문에서 문자열을 연결하거나, 문자열을 많이 연결할 일이 생긴다면 stringBuilder를 사용하는 것이 성능상 좋다.

이 표는 문자열 결합 방식의 성능 분석 표이다. (참고)

참고
https://inpa.tistory.com/entry/JAVA-%E2%98%95-String-%ED%83%80%EC%9E%85-%ED%95%9C-%EB%88%88%EC%97%90-%EC%9D%B4%ED%95%B4%ED%95%98%EA%B8%B0-String-Pool-%EB%AC%B8%EC%9E%90%EC%97%B4-%EB%B9%84%EA%B5%90

https://medium.com/@nagarjun_nagesh/maximizing-performance-in-java-string-concatenation-c588a7a125e8

https://siyoon210.tistory.com/160

profile
Record What I Learned

1개의 댓글

comment-user-thumbnail
2024년 8월 26일

최적화를 기가 막히게 해두셨다

답글 달기