# Java: String vs StringBuilder 비교
---
## 1. 개요
자바에서 문자열을 다룰 수 있는 대표 클래스는 `String`과 `StringBuilder`이다. 두 클래스 모두 문자열을 표현하는 데 사용되지만, \*\*불변성(immutability)\*\*과 ****메모리 구조****, ****성능**** 면에서 근본적인 차이가 존재한다.
- `String`: 불변(immutable) 객체. 문자열을 수정하는 것처럼 보이지만 실제로는 새로운 객체가 생성됨.
- `StringBuilder`: 가변(mutable) 객체. 동일 인스턴스 내의 버퍼를 직접 수정하여 효율적인 메모리 사용과 빠른 성능을 제공함.
이러한 차이로 인해, 문자열 처리 방식에 따라 두 클래스의 선택은 전혀 달라져야 한다.
---
## 2. 메모리 관점에서의 차이
### 2.1 String – 불변 객체의 메모리 동작
`String` 클래스는 한 번 생성된 이후로 내용을 절대 변경할 수 없는 ****불변 객체****이다. 문자열을 수정할 때마다 ****새로운 객체가 생성****되어 Heap 메모리에 누적된다.
#### 예시
```java
String s = "hi";
s = s + "!"; // "hi!"라는 새로운 String 객체가 생성됨
```
#### 메모리 흐름
```text
[Heap Memory]
"hi" ← 초기 객체
"hi!" ← 새로운 객체 (변수 s가 참조)
→ 기존 객체는 GC 대상이 되기 전까지 Heap에 남아 있음
→ 반복적인 수정 시 객체 수 증가 → GC 부하 및 메모리 낭비
```
#### 주요 특징
- 내부적으로 `char[]` 배열을 `final`로 선언하여 불변성 보장
- 멀티스레드 환경에서 안전하게 공유 가능
- 문자열 상수 풀(String Constant Pool)을 활용한 최적화 가능
---
### 2.2 StringBuilder – 가변 객체의 효율적 구조
`StringBuilder`는 내부에 ****동적으로 크기 조절이 가능한 char\[\] 버퍼****를 가지고 있으며, 문자열 조작 시 이 버퍼를 직접 수정한다. 불필요한 객체 생성이 없고, 성능과 메모리 측면에서 뛰어나다.
#### 예시
```java
StringBuilder sb = new StringBuilder("hi");
sb.append("!");
System.out.println(sb); // 출력: hi!
```
#### 메모리 흐름
```text
[Heap Memory]
StringBuilder 객체
└─ char[] buffer = ['h', 'i', '!'] ← 내부 버퍼가 직접 수정됨
→ 동일 객체 내에서 작업 → 메모리 재사용
→ 객체 수 증가 없음 → GC 부담 없음
```
#### 내부 구조 예시
```java
public final class StringBuilder {
char[] value;
int count;
public StringBuilder append(String str) {
ensureCapacityInternal(count + str.length());
str.getChars(0, str.length(), value, count);
count += str.length();
return this;
}
}
```
---
## 3. 성능 차이 비교
### 3.1 코드 성능 테스트
```java
// String (비효율적 방식)
String s = "";
for (int i = 0; i < 1000; i++) {
s += "a"; // 매번 새로운 객체 생성됨
}
```
```java
// StringBuilder (효율적 방식)
StringBuilder sb = new StringBuilder();
for (int i = 0; i < 1000; i++) {
sb.append("a"); // 내부 버퍼만 수정됨
}
```
### 3.2 성능 분석
항목
| String
| StringBuilder
|
---|
메모리 구조
| 불변 객체, 매번 새 객체 생성
| 가변 객체, 하나의 버퍼 사용
|
연결 방식
| `+`
또는
`concat()`
호출마다 새 객체
| `append()`
는 동일 객체에 누적
|
GC 부하
| 심함
| 적음
|
속도
| 느림
| 빠름
|
💡 ****결론****: 루프나 재귀에서 문자열을 누적해야 할 경우, `StringBuilder`가 수십 배 빠를 수 있음.
---
### 3.3 스레드 안전성 관점
- `String`: 불변 → 스레드 안전
- `StringBuilder`: 가변 + 동기화 없음 → 스레드 ****안전하지 않음****
- 다중 스레드에서 문자열 조작이 필요하다면 → `StringBuffer` 사용 고려
```java
// 멀티스레드 환경에서 StringBuilder는 위험
StringBuilder sb = new StringBuilder();
Runnable task = () -> {
for (int i = 0; i < 1000; i++) {
sb.append("x"); // 동기화 안 되어 경합 발생 가능
}
};
```
→ 해결책: `StringBuffer` 또는 `synchronized` 블록 사용
---
## 4. 결론 및 실무 가이드라인
### 4.1 어떤 클래스가 적절한가?
사용 상황
| 적절한 클래스
| 이유
|
---|
고정된 문자열 / 변경 없음
| `String`
| 불변성으로 안전하고, 메모리 캐싱 가능
|
루프 내 문자열 누적 / 대량 조작
| `StringBuilder`
| 메모리 효율 및 성능 우수
|
멀티스레드 환경에서 동기화가 필요한 경우
| `StringBuffer`
| 메서드 동기화로 스레드 안전성 확보
|
### 4.2 실전 팁
- ⚠ `String`으로 반복 연결하지 말 것 (예: `s = s + "a"` X)
- ✅ 대량 처리 시에는 반드시 `StringBuilder` 사용
- ✅ 멀티스레드 환경에서는 `StringBuffer` 또는 동기화 처리 필수
---