Skip to main content

Java: String vs StringBuilder vs StringBuffer 비교


1. 개요

자바에서 문자열을 다룰 때 흔히 사용하는 세 가지 클래스인 String, StringBuilder, StringBuffer는 모두 CharSequence 인터페이스를 구현하지만, 불변성, 동기화, 성능 측면에서 명확한 차이를 보인다.

  • String은 문자열 변경 시마다 새로운 객체를 생성 → 여러 객체가 Heap에 누적
  • StringBuilder와 StringBuffer는 동일 객체 내에서 내부 버퍼를 수정 → Heap 메모리를 효율적으로 사용
  • StringBuffer는 동기화를 위한 추가 비용으로 인해 메모리뿐 아니라 CPU 리소스도 더 소모됨

이 문서에서는 다음과 같은 관점에서 세 클래스의 차이점을 심층 분석한다.

  • 변경 가능 여부 (immutable vs mutable)
  • 성능 및 메모리 구조
  • 스레드 안전성 및 동기화 여부
  • 사용 예시 및 실제 적용 시 고려사항

2. String

2.1 불변 객체(Immutable)의 특징

  • String 객체는 **불변(immutable)**이다.
  • 문자열이 변경되는 것처럼 보이지만 실제로는 새로운 객체가 생성된다.
String str = "hello";
str += " world";
// 실제로는 "hello", "hello world" 두 개의 객체가 Heap에 존재

이유

  • 내부적으로 char[] 배열을 final로 가지고 있으며, 변경이 불가능
  • final class String → 상속 불가
  • 안전성, 보안, 캐싱, 문자열 상수 풀(String Pool) 최적화에 유리

2.2 성능 및 메모리 측면

  • 연결이 많을수록 비효율적: 반복적인 문자열 조작 시 매번 새로운 객체 생성
  • Garbage Collector 부하 증가

2.3 사용 예시

  • 단순 문자열 선언 및 불변 문자열 상수
  • 파라미터 값, 로그인 ID, 파일 경로 등 변경되지 않는 문자열

3. StringBuilder

3.1 가변 객체(Mutable) + 비동기 환경에 적합

  • 내부에 char[] 버퍼를 가지고 있으며, 동기화를 고려하지 않음
  • 성능과 메모리 측면에서 매우 효율적
StringBuilder sb = new StringBuilder("hello");
sb.append(" world");
System.out.println(sb.toString());  // hello world

3.2 주요 메서드

sb.append("a");
sb.insert(2, "x");
sb.replace(1, 3, "yz");
sb.delete(0, 2);
sb.reverse();

3.3 성능 테스트 예시

public class BuilderTest {
    public static void main(String[] args) {
        long start = System.currentTimeMillis();
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < 100_000; i++) {
            sb.append("abc");
        }
        long end = System.currentTimeMillis();
        System.out.println("StringBuilder 소요 시간: " + (end - start));
    }
}

3.4 사용 시점

  • 단일 스레드 환경에서 반복적인 문자열 조작
  • 파일 생성, 로깅 등 실시간 데이터 처리

4. StringBuffer

4.1 가변 객체(Mutable) + 스레드 안전(Thread-safe)

  • StringBuilder와 거의 동일하나, 모든 메서드가 synchronized 처리
  • 멀티스레드 환경에서 안정성 보장
StringBuffer sb = new StringBuffer("safe");
sb.append(" thread");
System.out.println(sb);  // safe thread

4.2 내부 구조

public synchronized StringBuffer append(String str) {
    super.append(str);
    return this;
}

4.3 성능 비교

  • 동기화 오버헤드로 인해 성능은 StringBuilder보다 낮음
  • 병렬 처리나 스레드풀 환경에서 안정성을 확보해야 할 때 적합

4.4 멀티스레드 예시

public class BufferThreadTest {
    static StringBuffer sb = new StringBuffer();

    public static void main(String[] args) throws InterruptedException {
        Runnable task = () -> {
            for (int i = 0; i < 1000; i++) {
                sb.append("a");
            }
        };

        Thread t1 = new Thread(task);
        Thread t2 = new Thread(task);
        t1.start(); t2.start();
        t1.join(); t2.join();

        System.out.println("최종 길이: " + sb.length());  // 2000 보장됨
    }
}

5. 성능 및 메모리 비교 요약

항목

String

StringBuilder

StringBuffer

변경 가능 여부

❌ 불변

✅ 가변

✅ 가변

동기화 지원

❌ 없음

❌ 없음

synchronized 지원

성능

느림 (매번 객체 생성)

빠름

중간 (동기화 오버헤드 존재)

메모리 효율성

낮음

높음

중간

적합한 환경

상수, 설정값, 로그 ID 등

단일 스레드 문자열 조작

멀티스레드 문자열 조작


6. 결론 및 실무 팁

  • String: 변경이 없는 정적 문자열 (상수, config 값 등)
  • StringBuilder: 반복문 내에서 문자열 누적, 대량 처리 시
  • StringBuffer: 동기화가 필요한 멀티스레드 환경에서만 사용

StringBuilder vs StringBuffer – 언제 뭐 써야 해?

항목

StringBuilderStringBuffer

스레드 안전성

❌ 비동기(스레드 안전 아님)

✅ 동기화(synchronized, 스레드 안전)

속도

빠름

상대적으로 느림

코딩 테스트/일반 개발

✅ 단일 스레드 환경에서 최적

❌ 보통 코테에선 불필요한 오버헤드 발생

멀티스레드 환경

❌ 사용 시 동기화 문제 생길 수 있음

✅ 멀티스레드 환경에서 안전하게 사용 가능

사용 예

코딩테스트, 파일 파싱, 로그 누적, 텍스트 생성

서버에서 공유 자원 처리, 멀티스레드 로그 등

  • 코딩 테스트, 단일 스레드 로직: StringBuilder 사용 (기본값)
  • 멀티스레드 환경 (예: 서버 개발): StringBuffer 사용

💡 JDK 1.5 이상에서는 StringBuilder를 기본으로 고려하고, 동기화가 꼭 필요한 경우에만 StringBuffer를 선택하자.