자바의 String 객체는 불변이기 때문에, 문자열을 수정하는 연산을 할 때마다
새로운 String 인스턴스가 생성됩니다
그렇기 때문에, 반복문에서 문자열을 누적하면 매 반복마다
새로운 문자열 객체가 힙 메모리에 쌓여 성능이 저하됩니다
이것이 문자열 수정 연산으로 인한 성능 이슈입니다
따라서 불변의 문제를 해결하기 위해,
문자열을 이어붙일 때는 String 대신 가변 문자열 클래스를 사용해야 합니다
그리고 그 대안으로 StringBuffer와 StringBuilder를 활용할 수 있습니다
StringBuffer는 멀티스레드 환경에서 사용할 수 있도록
내부 연산에 동기화(synchronized)가 적용되어 thread-safe합니다
다만, 동기화로 인한 약간의 성능 저하가 있습니다.
StringBuilder는 동기화를 지원하지 않지만, 그만큼 동작이 빠르고 가볍습니다
따라서 단일 스레드 환경이나 thread-safe가 필요 없는 경우 가장 좋은 선택입니다
JDK 5.0부터 자바 컴파일러는 문자열을 +로 더하는 코드를 자동으로
StringBuilder를 사용하도록 최적화합니다
예를 들어 str1 + str2 + str3
는 컴파일 시
new StringBuilder().append(str1).append(str2).append(str3).toString()
형태로 변환됩니다
하지만 컴파일러 최적화에도 불구하고, 반복문 안에서 +를 사용하면 여전히 비효율적일 수 있습니다.
매 반복마다 새로운 StringBuilder와 String 객체가 생성되기 때문입니다
해결책은 루프 밖에서 StringBuilder를 생성하고, 루프 안에서는 append()만 호출한 후
마지막에 toString()을 호출하는 방식입니다
Java 9부터 도입된 JEP 280("Indify String Concatenation")에 의해,
문자열 결합(+
연산)은 컴파일 타임이 아닌 런타임에 최적의 방법으로 수행됩니다.
StringBuilder
코드로 변환하지 않고,따라서 작은 규모의 문자열 결합은 개발자가 최적화하지 않아도 충분히 빠르게 동작합니다
그러나 매우 큰 문자열을 반복해서 연결하는 경우에는 여전히 명시적으로 최적화하는 것이 좋습니다
Java 9에서 도입된 JEP 254("Compact Strings") 기능을 통해, 문자열의 메모리 효율이 크게 개선되었습니다
ASCII 기반 문자만 포함된 문자열은 1바이트-per-문자 형식으로 저장됩니다.
그 외의 문자가 포함된 경우에는 기존처럼 2바이트-per-문자 형식으로 저장됩니다.
영어, 숫자, 공백 등으로 이루어진 대다수의 문자열에서 메모리 사용량이 최대 절반까지 줄어듭니다.
이를 통해 GC(Garbage Collection) 부하가 감소하며, 애플리케이션의 전반적인 성능 향상에 기여합니다.
방식 | 특징 | 성능 |
---|---|---|
StringBuilder | 명시적인 버퍼 관리, 반복적 문자열 생성에 최적 | 빠름 (반복적인 경우 가장 좋음) |
String.format | 가독성 및 포맷팅 편의성 좋음 | 상대적으로 느림 (성능이 중요한 경우 지양) |
문자열 + 연산 (invokedynamic) | 작은 규모 문자열 처리에 매우 효율적 | 빠름 (적당한 크기의 문자열) |
Java 21에서는 문자열 템플릿(JEP 430)이 제공됩니다.
더 간결하고 효율적인 문자열 처리를 지원합니다.
String name = "World";
String greeting = STR."Hello, \{name}!";
// 출력: "Hello, World!"
+
연산자를 적극 사용해도 성능상 문제 없습니다StringBuilder
사용을 권장합니다