3. 자꾸 String을 쓰지 말라는 거야
다음은 일반적으로 사용하는 쿼리 작성 문장이다.
String strSQL = "";
strSQL += "select * ";
strSQL += "from ( ";
strSQL += "select A_column, ";
strSQL += "B_column, ";
// 중간 생략(약 400라인)
...
이와 같은 메서드를 한 번 수행하면, 다음과 같은 메모리 사용량을 확인할 수 있다. | 구분 | 결과 | |–|–| | 메모리 사용량 | 10회 평균 약 5MB | | 응답 시간 | 10회 평균 약 5ms |
위 코드를 메모리 사용량과 응답 시간을 줄이기 위해 StringBuilder로 변경하였다.
StringBuilder strSQL = new StringBuilder();
strSQL.append(" select * ");
strSQL.append(" from ( ");
strSQL.append(" select A_column, ");
strSQL.append(" B_column, ");
// 중간 생략(약 400라인)
...
이렇게 변경한 후 수행한 결과는 다음과 같다.
구분 | 결과 |
---|---|
메모리 사용량 | 10회 평균 약 371KB |
응답 시간 | 10회 평균 약 0.3ms |
StringBuffer 클래스와 StringBuilder 클래스
StringBuffer 클래스나 StringBuilder 클래스에서 제공하는 메서드는 동일하다. 하지만 StringBuffer 클래스는 스레드에 안전하게(ThreadSafe) 설계되어 있으므로, 여러 개의 스레드에서 하나의 StringBuffer 객체를 처리해도 전혀 문제가 되지 않는다. 하지만 StringBuilder는 단일 스레드에서의 안전성만을 보장한다.
String vs. StringBuffer vs. StringBuilder
String, StringBuffer, StringBuilder 셋 중 어느 것이 가장 빠르고 메모리를 적게 사용할까?
- 10,000회 반복하여 문자열을 더하고, 이러한 작업을 10회 반복한다.
프로파일링 툴의 결과는 다음과 같다.
주요 소스 부분 | 응답 시간(ms) | 비고 |
---|---|---|
a+=aValue; | 95,801.41ms | 95초 |
b.append(aValue); | 247.48ms | 0.24초 |
c.append(aValue); | 174.17ms | 0.17초 |
그리고 메모리 사용량을 다음과 같다.
주요 소스 부분 | 메모리 사용량(bytes) | 생성된 임시 객체수 | 비고 |
---|---|---|---|
a+=aValue; | 100,102,000,000 | 4,000,000 | 약 95Gb |
b.append(aValue); | 29,493,600 | 1,200 | 약 28Mb |
c.append(aValue); | 29,493,600 | 1,200 | 약 28Mb |
응답 시간은 String보다 StringBuffer가 약 367배 빠르며, StringBuilder가 약 512배 빠르다. 메모리는 StringBuffer와 StringBuilder보다 String에서 약 3,390배 더 사용한다. 이러한 결과가 왜 발생하는지 알아보자.
a += aValue; // a = a + aValue
a에 aValue를 더하면 새로운 String 클래스의 객체가 만들어지고, 이전에 있던 a 객체는 필요 없는 쓰레기 값이 되어 GC 대상이 되어 버린다. 이러한 작업이 반복 수해오디면서 메모리를 많이 사용하게 되고, 응답 속도에도 많은 영향을 미치게 된다. GC를 하면 할수록 시스템의 CPU를 많이 사용하게 되고 시간도 많이 소요된다.
- String은 짧은 문자열을 더할 경우 사용한다.
- StringBuffer는 스레드에 안전한 프로그램이 필요할 때나, 개발 중인 시스템의 대부분이 스레드에 안전한지를 모를 경우 사용하면 좋다. 만약 클래스에 static으로 선언된 문자열을 변경하거나, singleton으로 선언된 클래스에 선언된 문자열일 경우에는 이 클래스를 사용해야만 한다.
- StringBuilder는 스레드에 안전한지의 여부와 전혀 관계 없는 프로그램을 개발할 때 사용하면 좋다. 만약 메서드 내에 변수를 선언했다면, 해당 변수는 그 메서드 내에서만 살아있으므로, StringBuilder를 사용하면 된다.