String은 자바에서 가장 흔히 쓰이는 클래스 중 하나이고, StringBuilder와 StringBuffer는 String을 다룰 수 있는 좋은 도구입니다. 이 게시글은 String과 StringBuilder, 그리고 StringBuffer 간의 차이를 정리하기 위해 작성되었습니다.
String
1. 자바에서 String 클래스는 문자 string을 나타내며, 두 가지 방식으로 이를 인스턴스화할 수 있습니다.
String str = "ABC";
// or
String str = new String("ABC");
2. Java에서 String은 불변 객체로 멀티 스레드 환경에 적합합니다. 데이터 비일관성에 대한 걱정이 없기 때문에 함수 간에도 공유할 수 있습니다.
3. 큰따옴표(")로 String을 정의할 때, JVM은 String Pool 내에 같은 값이 있는지 먼저 찾아봅니다. 만약 발견한다면, 풀에서 String 객체에 대한 참조를 반환합니다. 그렇지 않다면, String Pool 내에서 새로운 객체를 생성하고, 참조를 반환합니다. 이처럼 JVM은 서로 다른 스레드 내에서 같은 String을 공유함으로써 대량의 메모리를 절약할 수 있습니다.
4. String을 생성하기 위해 새로운 연산자가 사용된다면 힙 메모리 내에서 생성됩니다.
5. + 연산자를 통해 String에 대해 오버로드되며, 이를 통해 두 String을 합칠 수 있지만 내부적으론 StringBuffer를 통해 이를 수행합니다.
자바에서 String은 불변이기 때문에, concatenation, substring 등의 조작을 수행할 때마다 새로운 String 객체를 생성하고 이전에 사용하던 String을 Garbage Collection(이하, GC)에 폐기합니다. 이것은 무거운 연산이고, 실제로 힙 영역에 많은 garbage를 생성합니다. 이를 해결하기 위해 Java는 StringBuffer와 StringBuilder를 제공합니다.
이들은 가변 객체로 String 조작을 위해 append(), insert(), delete(), substring() 메서드를 제공합니다.
StringBuffer vs. StringBuilder
StringBuffer는 Java 1.4 버전까지 String 조작을 위해 사용할 수 있는 유일한 선택지였지만, 모든 public 메서드들이 동기화 방식이라는 단점이 있었습니다. 이를 '수행 비용이 들지만 thread safety를 제공한다'라고 표현할 수 있습니다.
대부분의 경우, String은 멀티 스레드 환경에서 사용되지 않습니다. 그래서 Java 1.5 버전에서는 thread safety와 동기화가 없는 StringBuffer와 같은 StringBuilder 클래스가 새로 생성되었습니다. StringBuilder 클래스는 싱글 스레드 환경 및 thread safety를 신경 쓰지 않는 경우 주로 사용됩니다.
StringBuffer | StringBuilder | |
Thread Safety | O | X |
Synchronization | O | X |
Generated | Java 1.0 ~ | Java 1.5 ~ |
Speed | Slower | Faster |
StringBuilder와 StringBuffer의 성능을 검증하기 위해 다음 코드를 5번 실행했을 때의 수행 결과를 비교해 보자.
import java.util.GregorianCalendar;
public class TestString {
public static void main(String[] args) {
System.gc();
long start = new GregorianCalendar().getTimeInMillis();
long startMemory = Runtime.getRuntime().freeMemory();
StringBuffer sb = new StringBuffer();
// StringBuilder sb = new StringBuilder();
for(int i = 0; i < 10_000_000; i++) {
sb.append(":").append(i);
}
long end = new GregorianCalendar().getTimeInMillis();
long endMemory = Runtime.getRuntime().freeMemory();
System.out.println("Time Taken:" + (end - start));
System.out.println("Memory used:" + (startMemory-endMemory));
}
}
반복 횟수 | StringBuffer (Time, Memory) | StringBuilder (Time, Memory) |
1,000,000 | 182, -12361088 | 179, -12352888 |
10,000,000 | 1126, -79177424 | 843, -156605152 |
이를 통해 Single thread 환경에서 StringBuilder는 StringBuffer 보다 수행 속도가 빠르다는 것을 확인할 수 있습니다.
정리하면, String은 불변 객체로 String에 대한 + 연산을 수행하기 위해 StringBuffer와 StringBuilder 클래스가 생성되었습니다. 이 중, StringBuffer는 thread safe 하고 동기화 처리하기 때문에 StringBuilder에 비해 속도가 느립니다. StringBuiilder는 String 조작 시 멀티 스레드가 아닌 환경에서 사용하는 것이 유리합니다.
Q1. String과 StringBuffer, 그리고 StringBuilder를 비교해 주세요.
String은 불변 객체로 멀티 스레드 환경에서 변하지 않는 String 값을 저장하는 데 유용합니다. StringBuffer와 StringBuilder는 불변 객체인 String을 유연하게 다루기 위해 생성된 것으로 StringBuffer는 동기화 처리를 통해 thread safety를 제공하지만 그로 인해 속도가 상대적으로 느린 반면, StringBuilder는 thread safety를 보장하지 않는 대신 빠른 연산을 보장하기 때문에 주로 싱글 스레드 환경 및 동기화를 고려하지 않아도 되는 경우 주로 사용됩니다.
Q2. String이 불변 객체인 이유에 대해 설명해 주세요.
String을 불변 객체로 설계한 이유는 캐싱, 보안, 동기화 및 성능 측면에서 이점을 얻기 위해서입니다.
String을 불변하게 함으로써 String pool에 각 리터럴 문자열의 하나만 저장하여 이를 다시 사용하거나 캐싱에 이용함으로써 힙 메모리를 절약할 수 있습니다.
데이터 베이스 사용자 이름 및 암호는 데이터베이스 연결을 수신하기 위해 문자열로 전달되는데, 만약 이것이 변경 가능하다면 해커가 참조 값을 변경하여 애플리케이션 보안 문제를 일으킬 수 있습니다
마지막으로, 항상 불변하기 때문에 동시에 실행되는 여러 스레드에서도 안정적인 공유가 가능합니다.
Q3. Thread safety에 대해 설명해 주세요.
Thread safety는 멀티 스레드 환경에서 안전하게 동작할 수 있는지 여부를 나타내는 것으로, StringBuffer는 동기화 지원으로 인해 스레드 내에서 안전한 반면, StringBuilder는 동기화를 지원하지 않아 스레드 내에서 안전하지 않습니다.
참고 출처
'프로그래밍 언어 > Java' 카테고리의 다른 글
[Java] OOP의 4가지 특징 (0) | 2024.01.23 |
---|---|
[Java] 자바 접근 제어자의 유형과 특징 (0) | 2024.01.22 |
[Java] 자바의 컴파일 과정 & JVM 메모리 구조 (0) | 2024.01.17 |
[Java] Garbage Collector란? (1) | 2023.11.06 |
[Java] JRE & JDK & JVM (0) | 2023.10.17 |