본문 바로가기

Dev.BackEnd/JAVA

[JAVA Adv] StringBuffer vs StringBuilder 그리고 String


StringBuffer vs StringBuilder

String 의 정체
두 클래스의 차이를 알기 위해서 일단 String 객체의 정체를 짚어보고 가자.
String 객체는 기본적으로 immutable 이다. 내부적으로 char[] 배열을 사용하고 있고 이 배열은 변경이 불가능하다는 뜻이다. 즉, Constant Pool에 문자열이 특정 상수로 저장되어 더 이상 변하지 않는, 변할 수 없는 값이 되는 것이다. (물론 참조 변수인 String 객체는 Heap 영역에 생성된다! String 객체 자체가 Constant Pool에 생성된다는 것이 아니다.)

이 String 객체에 문자열을 저장하고, + 연산자 또는 concat 메소드를 통해 새로운 문자열을 덧붙이려고 할 때는, 기존의 String 객체가 가리키고 있던 문자열이 복사되고, 새로운 문자열이 덧붙여져 새로운 String 객체가 반환된다. 즉, 기존의 값은 사라지지 않고 기존의 값을 가리키던 변수가 새로 합쳐진 형태의 값을 가리키게 되는 것이다. 기존의 값은 특정 시점에 Garbage Collection에 의해 회수된다.

String의 정체를 알고나니 엄청난 메모리 낭비가 발생하고 있는 것 같다.
그리고 직관적으로 매우 느릴 것 같다. (실제로 느리다고 한다.)

그렇다면 String 객체는 왜 이렇게 디자인되어 있는 것인가?
일단 immutable 객체는 thread-safe 하다. 여러 개의 스레드가 동시에 접근하여 읽어도 변하지 않는 값이기 때문에 문제가 발생하지 않는다. 때문에 동기화 문제를 고려하지 않고 내부 데이터를 자유롭게 공유할 수 있다는 장점이 있다.


StringBuffer vs StringBuilder
일단 StringBuffer 와 StringBuilder 두 경우는 새로운 문자열을 concat하는데 기존의 String과는 다르게 작동한다. 새로운 객체를 생성하지 않고 기존 객체의 버퍼 크기를 늘리며 문자열을 추가한다. 즉 immutable 했던 String 객체와는 다르게 mutable 한 것이다.

그렇다면 동일한 역할을 수행하는 두 keyword의 차이는 무엇인가?
바로 멀티스레딩 환경에서의 동기화 문제를 어떻게 처리하는지가 다르다.

먼저 StringBuffer를 살펴보자.
StringBuffer는 각 메소드별로 synchronized keyword가 존재한다. 그렇기 때문에 멀티스레딩 환경에서 thread-safe하다. 반대로 StringBuilder는 동기화 처리를 하지 않는다. 하지만 synchronized keyword가 없기 때문에 성능이 빠르다. 멀티스레딩 환경이 아닐 경우에 StringBuffer 클래스를 사용한다면, 성능이 나빠질 수 있기 때문에 이 점을 고려하여 클래스를 사용하여야 한다.

최근 JDK 버전(JDK 1.5version 이후)에서는 StringBuilder를 사용하지 않아도 된다.
문자열 연산에 String 클래스를 사용하더라도 컴파일 단계에서 StringBuilder로 컴파일 되어 작동하도록 되었기 때문이다.
때문에 따로 StringBuilder를 사용할 이유가 없어진 것이다.


JDK에는 StringBuffer와 StringBuilder처럼 동기화 문제를 고려하여 같은 역할을 수행함에도 두 종류의 클래스가 존재하는 클래스들이 또 있다. 즉, thread-safe를 위해 메소드에 synchronized keyword가 있고 없고의 차이만 있고 동일한 목적으로 사용하는 클래스가 존재한다는 것이다. HashTable과 HashMap이 그 예가 되겠다. 동기화 보장이 필요하다면 HashTable을 사용하고 그럴 필요가 없으면 HashMap을 사용한다.


추가 자료> JVM 구조에 대해서 


end