해당 포스트는 "자바 성능 튜닝 이야기" 책의 내용을 요약한 것이다.



- StringBuffer 클래스

: StringBuffer 클래스는 스레드에 안전하게 설계되어 있다. 따라서 여러 개의 스레드에서 하나의 StringBuffer 객체를 처리해도 전혀 문제가 되지 않는다. StringBuffer는 스레드에 안전한 프로그램이 필요할 때나, 개발 중인 시스템의 부분이 스레드에 안전한지 모를 경우 사용하는 게 좋다. 만약 static으로 선언한 문자열이거나 singleton으로 선언도니 클래스에 선언된 문자열의 경우에는 StringBuffer 클래스를 사용하는 게 좋다.


- StringBuilder 클래스

: StringBuilder 클래스는 단일 스레드에서의 안전성만 보장한다. 여러 개의 스레드에서 하나의 StringBuilder 객체에 접근하면 문제가 발생한다. 따라서 StringBuilder는 스레드와 상관 없는 프로그램을 개발할 때 사용하는 게 좋다. 메서드 내의 변수로만 사용할 시, 해당 변수는 메서드 내에서만 살아있으므로, StringBuffer을 사용하면 된다.


- String

: 짧은 문자열을 더할 경우에 사용한다.


*CharSequence 

: StringBuffer 클래스나 StringBuilder 클래스의 경우 생성자 매개변수로 CharSequence를 받는다. CharSequence는 인터페이스로 클래스가 아니기 때문에 객체 생성이 불가능하다. String, StringBuffer, StringBuilder 클래스가 CharSequence 인터페이스를 구현하고 있다. 따라서 StringBuffer나 StringBuilder 클래스를 생성할 때 매개변수로 String, StringBuffer, StringBuilder 객체를 전달할 수 있다. 


ex)

final String aValue="abcde"; for(int outLoop=0; outLoop<10; outLoop++){ String a = new String(); StringBuffer b = new StringBuffer(); StringBuilder c = new StringBuilder(); for(int loop=0; loop<10000; loop++){ a+=aValue; } for(int loop=0; loop<10000; loop++){ b.append(aValue); } String temp=b.toString(); for(int loop=0; loop<10000; loop++){ c.append(aValue); } String temp2 = c.toString(); }

String, StringBuffer, StringBuilder 객체에 대해서 각각 for문을 통해 10000번씩 aValue 값을 더해주고 이를 outLoop 10번을 수행한다. 이 프로그램을 10번 반복해서 실행한다. 그러면 10000*10*10으로 문자열을 더하는 각 라인들은 100만번씩 수행된다. 이를 수행한 결과는 다음과 같다.

주요 소스 부분 

응답 시간(ms) 

비고 

a+=aValue; 

95,801.41ms 

95초 

b.append(aValue);

String temp = b.toString(); 

 247.48ms

14.21ms

0.24초 

 c.append(aValue);

String temp2 = c.toString();

174.17ms

13.38ms 

0.17초 


다음은 메모리 사용량이다.

 주요 소스 부분

메모리 사용량(bytes) 

생성된 임시 객체수 

비고 

 a+=aValue;

100,102,000,000 

4,000,000 

약 95Gb 

 b.append(aValue);

String temp = b.toString();

29,493,600

10,004,000 

1,200

200 

약 28Mb

약 9.5Mb 

c.append(aValue);

String temp = c.toString() 

29,493,600

10,004,000 

1,200

200 

약 28Mb

약 9.5Mb 


위와 같은 상황이 발생하는 이유는 String 객체의 경우 문자열을 더하는 소스를 실행할 때마다 새로운 객체를 생성한 후 더해진 문자열을 대입한다. 즉, 기존의 문자열은 쓰레기가 된다. 이러한 과정을 반복 수행하면 메모리를 많이 사용하게 되고 응답 속도에도 영향을 많이 미칠 수 밖에 없다. 반면에 StringBuffer와 StringBuilder의 경우는 String과 다르게 문자열을 더하는 코드가 실행될 때 새로운 객체를 생성하지 않고 기존에 있는 객체의 크기를 증가시키면서 더한다. 


* 참고로 b.append(aValue)가 아닌 b.append(aValue+"더하기") 와 같이 +연산자를 사용할 경우 StringBuffer/StringBuilder를 사용해 메모리 사용량과 속도를 줄이는 효과가 전혀 없어진다. 따라서 StringBuffer나 StringBuilder에서 문자열을 더할 때 되도록 append를 이용해서 문자열을 더해야 한다. 

+ Recent posts