이번에는 자바에서 많이 사용하게 되는 문자열 Class에 대해 알아보자.


Java에서 제공하는 문자열 Class String, StringBuffer, StringBuilder들이 있다.


보통은 String Class를 사용하지만 Java를 좀더 Deep 하게 알게 되고, 문자열 처리에서 시간이 너무 많이 걸렸던 문제를 경험하게 되면 StringBuffer를 사용하게 된다. 두 문자열 Class의 차이는 메모리 처리인데 그로 인해 상황에 따라 성능 차이가 발생하게 된다.


그럼 각 Class의 차이점에 대해 알아보자.


간단히 보면 아래 표와 같다.


String

StringBuffer

StringBuilder

Immutable(불변함)

Mutable(변함, 변하기 쉬움)

 

synchronized

unsynchronized


그럼 차이점을 기준으로 하나씩 이야기 해보겠다.


먼저 String StringBuffer/StringBuilder의 차이점부터 확인해보자.


String은 변경이 불가능한 immutable 클래스이다. (String는 내부적으로 char[]를 사용하는데 이 배열은 변경이 불가능하다. ) , 한번 생성되면 String 객체가 변할 수 없다. 여기서 변경이 불가능하다는 말이 무슨 뜻일까?


먼저 아래 소스를 보자.



String str = "Hello";

        str = str.concat(" World");

    str += " JAVA";



위에서 문자열 처리에 사용한 concat() 메소드와 “+” 연산자를 보면 처음 생성한 str String 객체가 변경이 가능한 것처럼 보인다. 하지만 String Class의 동작 원리를 보면 원래 객체와 다른 새로운 String 객체를 만들어 반환해 준다. 새로 String 객체가 생성되기 때문에 기존 String 객체가 가지고 있는 문자열은 변경되지 않은 체 사용 가능한 상태로 남아있는다

아래 그림을 통해 이해해 보자.

str에 문자열이 더해질 때 마다 추가한 문자열이 합쳐진 String 객체가 새로 생성하여 str 변수에 해당 객체의 주소 정보로 갱신해 주는 것이다.  다시 설명 하면 처음 “Hello” 문자열을 가진 객체의 내용을 바꾸는 게 아니라 새 String 객체를 만들어 “Hello World” 라는 문자열 저장하고 객체의 주소를 str 변수에 다시 할당해 주는 것이다. (, String 클래스 객체는 Heappermanent area(Garbage Collection 대상 영역)에 생성되며, 한 번 생성된 객체의 내부 내용을 변화시킬 수 없다그리고 기존 객체를 사용하는 곳이 없으면 GC로 회수된다.)


위와 같은 동작 원리 때문에 String Class의 변경은 객체를 생성하기 위한 시간과 메모리를 낭비하게 되고, GC의 발생 빈도를 높아지게 된다.


하지만 Immutable ClassString Class에서 문자열은 오직 생성자에서만 생성이 가능하고 변경이 불가능하기 때문에 변경을 원한다면, 원하는 값을 가진 새로운 객체를 생성해야 한다. 그래서 변경은 적고 읽기만 많은 경우, 또는 여러 쓰레드나 객체에서 공유하는 경우 synchronization(동기화) 와 같은 특별한 안정장치 없이도 안전하게 공유할 수 있게 된다.


이런 이유 때문에 대부분의 문자열이 복잡한 문자열 처리과정보다는 한번 설정된 문자열들을 여러 곳에서 공유하는 경우가 많으므로 자바에서 기본 문자열을 처리하는 클래스로 String 클래스를 immutable 패턴으로 설정했다.


그럼 이제 Mutable ClassStringBuffer/StringBuilder 클래스에 대해 알아보자.


StringBuffer/StringBuilder Classappend(), insert(), delete() 등의 메소드를 통하여 StringBuffer/StringBuilder 객체가 가지고 있는 문자열을 변경 할 수 있으며, Mutable Class이기 때문에 String Class처럼 새로운 객체를 생성하지 않고 기존의 문자열을 변경한다. (String과 마찬가지로 내부적으로 char[]을 사용하지만 이 값은 변경이 가능하다.)


먼저 아래 소스를 보자


StringBuffer sb = new StringBuffer();

        sb.append("Hello");

        sb.append(" World");

        sb.append(" JAVA");


위 코드를 실행하면 StringBuffer/StringBuilder new 연산자가 한번만 실행하게 된다. 다시 풀어 쓰면 한번 생성된 StringBuffer/StringBuilder 객체 크기를 증가시키면서 값을 더한다. 아래 그림의 통해 StringBuffer/StringBuilder Class가 어떻게 동작하는지 이해해 보자.

sb 변수에 처음 할당된 StringBuffer/StringBuilder() 객체의 주소의 변경 없이 기존 객체의 공간이 부족하게 되면 기존 버퍼의 크기를 증가 시키면서 새로운 문자열을 더하고 있다. 위와 같이 객체 생성을 생성하지 않는 원리 때문에 String 클래스 보다 효율적이라고 생각하기 쉽다. 하지만 동기화 처리 때문에 단순 참조일 경우에는 상대적으로 성능이 낮아 진다.


그럼 이제 StringBuffer StringBuilder 차이에 대해 알아보자. 기본적으로 두 클래스가 제공하는 메소드는 동일하지만 차이점은 StringBuffer는 멀티 쓰레드 상태에서 동기화를 지원하지만 StringBuilder는 동기화를 지원하지 않는다는 것이다. StringBuffer에서 제공하는 메소드에는 synchronized 적용되고 있다는 것이다. Class 소스 일부를 보면 아래와 같다.


public final class StringBuffer {

 public synchronized StringBuffer append(String str) {

super.append(str);

return this;

}

[...]

}

public final class StringBuilder {

public StringBuilder append(String str) {

super.append(str);

return this;

}

[...]

}



같은 메소드이지만 StringBuffer Class의 메소드는 synchronized keyword가 있는 걸 확인할 수 있다. 이로 인해 ThreadSafe한 설계가 가능해 진다. 그래서 여러 개의 스레드에서 하나의 StringBuffer 객체를 처리해도 문제가 되지 않는다. 그와는 반대로 StringBuilder Class의 메소드들은 ThreadSafe 하지 않기 때문에 여러 개의 스레드에서 하나의 StringBuilder 객체를 처리하게 문제가 발생하게 된다.


그렇기 때문에 멀티스레드 환경이라면 값 동기화 보장을 위해 StringBuffer를 사용하고, 단일스레드 환경이라면 StringBuilder를 사용하는 것이 좋다. 단일스레드환경에서 StringBuffer를 사용해도 문제는 없지만 동기화 처리 때문에 StringBuilder에 비해 성능이 떨어진다.


JDK 1.5 버전 이전에서는 문자열 연산(+, concat)을 할 때에는 조합된 문자열을 새로운 메모리에 할당하여 참조함으로 인해서 성능상의 이슈가 있었다그러나 JDK 1.5 버전 이후부터는 컴파일 단계에서 String 객체를 사용하더라도 StringBuilder로 컴파일 되도록 변경되었다그래서 JDK 1.5 이후 버전에서는 String 클래스를 활용해도 StringBuilder와 성능상으로 차이가 없어졌다.


단순한 성능만 놓고 본다면 연산이 많은 경우 아래와 같이 된다.


StringBuilder > StringBuffer >>> String


하지만 사용하는 환경에 따라 성능이 차이가 나기 때문에 다양한 고려 사항을 고민해서 선택해서 사용 해한다. 간단하게 정리하면 아래와 같다.


-       멀티 쓰레드 환경에서 하나의 문자열에 대하여 다른 문자나 문자열의 추가가 많이 발생할 경우 StringBuffer 클래스를 사용하는 게 유리하다.

-       멀티 쓰레드 환경에서 하나의 문자열에 대하여 다른 문자나 문자열의 추가가 많이 발생할 경우 StringBuilder 클래스를 사용하는 게 유리하다.

-       String 클래스는 문자열의 + 연산 회수가 많지 않을 경우에 유리하다.

참고 사이트
  • http://javacan.tistory.com/entry/39
  • http://skynaver.tistory.com/entry/String%EA%B3%BC-StringBuffer%EC%9D%98-%EC%B0%A8%EC%9D%B4%EC%A0%90
  • https://slipp.net/questions/271
  • http://fowler.egloos.com/1243657


'IT > JAVA' 카테고리의 다른 글

GC 튜닝 절차  (0) 2018.11.09
Java Method Signature  (0) 2017.12.08
VO vs DTO  (0) 2017.01.12
Annotaion  (0) 2016.05.09

+ Recent posts