level up 과정 [JAVA]

58. java String 객체에 대한 이해 (StringBuilder,StringBuffer 등을 사용해야 하는 이유 및 기타 String의 특징)

참고문헌 : 자바 성능을 결정짓는 코딩 습관과 튜닝 이야기

StringBuffer 클래스와 StringBuilder 클래스

JDK 5.0 기준으로 문자열을 만드는 클래스는 String, StringBuffer, StringBuilder가 가장 많이 사용된다. 여기서 StringBuilder 클래스는 JDK 5.0에서 새로 추가되었다. StringBuffer 클래스나 StringBuilder 클래스에서 제공하는 메소드는 동일하다. 그러면 StringBuffer 클래스와 StringBuilder 클래스는 뭐가 다를까? 
StringBuffer 클래스는 스레드에 안전하게(ThreadSafe) 설계되어 있으므로, 여러 개의 스레드에서 하나의  StringBuffer 객체를 처리해도 전혀 문제가 되지 않는다. 하지만  StringBuilder 는 단일 스레드에서 안전성만을 보장한다. 그렇기 때문에 여러 개의 스레드에서 하나의 StringBuilder  객체를 처리하면 문제가 발생한다.

 

StringBuffer 클래스 생성자
생성자 설명
StringBuffer() 아무 값도 없는 StringBuffer 객체를 생성한다. 기본 욜량은 16개의 char이다. 
StringBuffer(CharSequence seq) CharSequence를 매개변수로 받아 그 seq 값을 갖는 StringBuffer를 생성한다.
StringBuffer(int capacity) capacity에 지정한 만큼의 용량을 갖는 StringBuffer를 생성한다.
StringBuffer(String str) str의 값을 갖는 StringBuffer를 생성한다.
<StringTest.java>

public class StringTest {

    final static String aValue = "abcde";

	public static void main(String[] args) 
	{
		for(int outLoop = 0; outLoop < 3; outLoop++) {
			String a = new String();
			StringBuffer b = new StringBuffer();
			StringBuilder c = new StringBuilder();

			long startTime = System.currentTimeMillis();
			for (int loop = 0; loop < 100000; loop++)
			{
				a += aValue;
			}
			System.out.println("# String : ["+outLoop+"] "
                +((float)(System.currentTimeMillis()-startTime)/1000));

			startTime = System.currentTimeMillis();
			for (int loop = 0; loop < 100000; loop++)
			{
				b.append(aValue);
			}
			String temp = b.toString();
			System.out.println("# StringBuffer : ["+outLoop+"] "
                +((float)(System.currentTimeMillis()-startTime)/1000));

			startTime = System.currentTimeMillis();
			for (int loop = 0; loop < 100000; loop++)
			{
				c.append(aValue);
			}
			String temp2 = c.toString();
			System.out.println("# StringBuilder : ["+outLoop+"] "
                +((float)(System.currentTimeMillis()-startTime)/1000));
		}
	}
}  
---------- Java Exec ----------
# String : [0] 76.758
# StringBuffer : [0] 0.0040
# StringBuilder : [0] 0.0040
# String : [1] 79.899
# StringBuffer : [1] 0.0050
# StringBuilder : [1] 0.0030
# String : [2] 81.259
# StringBuffer : [2] 0.0050
# StringBuilder : [2] 0.0040
출력 완료 (3분 58초 경과) - 정상 종료
 

위와 같은 결과가 왜 발생하는지 알아보자.

a += aValue; //(이것은 a = a + aValue와 같다.)

이 소스 라인이 수행되면 어떻게 될까? 여기에 우리가 지금까지 간과해 온 부분이 존재한다. a 에 aValue를 더하면 새로운 String 클래스 객체가 만들어지고, 이전에 있던 a 객체는 필요 없는 쓰레기 값이 되어 GC의 대상이 되어 버린다.

a += aValue 값(첫 번째 수행) : abcde
a += aValue 값(두 번째 수행) : abcdeabcde
a += aValue 값(세 번째 수행) : abcdeabcdeabcde

그림으로 나타내면 다음과 같다.

 가장 처음에 'abcde' 값이 저장되어 있던, 더해지기 전의 a 객체는 a += aValue; 를 수행하면서 사라지고(쓰레기가 되고), 새로운 주소와 'abcdeabcde'라는 값을 가지는 a 객체가 생성된다. 세 번째 수행되면 'abcdeabcdeabcde' 라는 값을 가지는 또 다른 새로운 객체가 만들어진다. 이런 작업을 수행하면서 메모리를 많이 사용하게 되고, 응답속도에도 많은 영향을 미치게 된다. GC를 하면 할수록 시스템의 CPU를 사용하게 되고 시간도 많이 소요된다. 그래서 프로그래밍을 할 때, 메모리 사용을 언제나 최소화하는 것은 당연한 일이다.

그러면 StringBuffer나 StringBuilder는 어떻게 작동되는지 알아보자. 이 두 가지 클래스가 동작하는 원리는 다음 그림과 같이 나타낼 수 있다.

StringBuffer나 StringBuilder는 String과는 다르게 새로운 객체를 생성시키지 않고, 기존에 있는 객체의 크기를 증가시키면서 값을 더한다. 이러한 String의 특성을 어떻게 튜닝에 적용할까?

  • String은 짧은 문자열을 더할 경우 사용한다.
  • StringBuffer는 스레드에 안전한 프로그램이 필요할 때나, 개발 중인 시스템의 부분이 스레드에 안전한지 모를 경우 사용하면 좋다.
  • StringBuilder는 스레드에 안전한지 여부가 전혀 관계 없는 프로그램을 개발할 때 사용하면 좋다.

댓글

댓글 본문
버전 관리
어디다쏨
현재 버전
선택 버전
graphittie 자세히 보기