무슨 차이가 있을지 궁금해서 정리해봄
- Java에서 문자열(String) 처리할 때 성능 차이가 중요한 이유
- 작은 코드 차이가 대규모 프로젝트에서 성능 문제로 이어질 수 있음
- 이번 글에서는 메모리 구조, 동시성, 성능 측정 예제를 중심으로 비교
String, StringBuilder, StringBuffer 차이
구분 특징 동기화 사용 추천 상황
| String |
불변(immutable) |
- |
자주 변경되지 않는 문자열, 안전성 필요할 때 |
| StringBuilder |
가변(mutable) |
X |
단일 스레드에서 문자열 반복 변경 시 |
| StringBuffer |
가변(mutable) |
O (synchronized) |
멀티스레드 환경에서 안전하게 문자열 처리 |
- 핵심: String은 불변 → 매번 새로운 객체 생성, StringBuilder/Buffer는 내부 버퍼에서 처리 → 성능 좋음
메모리 구조
- String
- JVM 힙 영역 + String Pool 활용
- 불변 → 문자열 변경 시 새로운 객체 생성 → GC 부담
- StringBuilder/StringBuffer
- 내부 char 배열 사용
- append() 등 반복 변경 시 재할당 최소화
- 동기화 여부에 따라 성능 차이 발생
동시성 문제
- StringBuilder: 멀티스레드 접근 시 문제 발생 가능
- StringBuffer: synchronized 처리 → 안전하지만 단일 스레드보다 느림
성능 비교 실험 예제
public class StringPerformanceTest {
public static void main(String[] args) {
int n = 100_000;
// String
long start = System.currentTimeMillis();
String str = "";
for (int i = 0; i < n; i++) {
str += "a";
}
System.out.println("String time: " + (System.currentTimeMillis() - start));
// StringBuilder
start = System.currentTimeMillis();
StringBuilder sb = new StringBuilder();
for (int i = 0; i < n; i++) {
sb.append("a");
}
System.out.println("StringBuilder time: " + (System.currentTimeMillis() - start));
// StringBuffer
start = System.currentTimeMillis();
StringBuffer sbuf = new StringBuffer();
for (int i = 0; i < n; i++) {
sbuf.append("a");
}
System.out.println("StringBuffer time: " + (System.currentTimeMillis() - start));
}
}
- 예상 결과: String >> StringBuffer > StringBuilder (단일 스레드 기준)
실제 프로젝트에서의 문제 & 해결
- 문제: 반복 문자열 연결 → GC 과다 발생 → 성능 저하
- 해결: StringBuilder 사용, 단일 스레드에서 append, StringBuffer는 멀티스레드 상황만 사용
- 팁: log 문자열 처리, JSON 문자열 생성 등 반복 처리 많은 코드에서 성능 개선 가능
결론
- String: 불변 → 안전하지만 성능 낮음
- StringBuilder: 단일 스레드에서 최고 성능
- StringBuffer: 멀티스레드 안전, 약간 성능 저하
- 핵심: 상황에 맞는 선택이 중요