Java String - Optimizer
앞서 가변 문자열과 불변 문자열을 살펴보았다.
이번엔 문자열에 대한 최적화를 알아보도록 한다.
먼저 대부분의 최적화는 자바 컴파일러가 알아서 처리한다.
문자열 리터럴을 합칠 때 컴파일은 문자열을 자동으로 합쳐준다.
컴파일 전
String start = "Hello, " + "World";
컴파일 후
String start = "Hello, World";
따라서 런타임에서의 결합 연산을 수행되지 않으므로 성능이 향상된다.
코드의 결과는 같지만 다르게 최적화 하는 코드를 살펴보자.
String ret = str1 + str2;
변수로 문자열을 합치는 경우 그 값이 어떤 값이 있는지 컴파일 시점에서는 알 수 없으므로 컴파일러는 단순 합치지 않는다.
이러한 경우 다음과 같이 컴파일러는 다음과 같이 최적화를 시도한다. (자바 버전에 따라 다르게 동작할 수 있음)
String ret = new StringBuilder().append(str1).append(str2).toString();
앞서 문자열을 간단하게 합쳐서 동작하는 코드를 살펴보았다.
문자열을 합칠 때는 StringBuilder 사용하지 않더라도 연산자(+)를 사용하면 컴파일러가 알아서 최적화를 수행한다.
String 최적화 어려운 상황
자바 버전 따라서 for문과 같은 루프 블록에서 더하는 경우 최적화가 이루어지지 않는다.
long startTimer = System.currentTimeMillis();
String result = "";
for (int i = 0; i < 1000000; i++) {
result += "Hello Java ";
}
long endTimer = System.currentTimeMillis();
System.out.println("result = " + result);
System.out.println("time = " + (endTimer - startTimer) + "ms");
...Java Hello Java Hello Java
time = 4735ms
위 코드는 for 문에서 연산자(+)로 문자열을 합치고 있다.
그리고 for문 10만번 모두 돌린 경우의 시간을 측정한 코드이다.
문자열을 합치는 컴파일러 최적화는 앞서 설명대로 새 객체로 만드는 new 키워드로 StringBuilder 객체로 생성한다고 설명하였다. 이어서 append 메소드로 문자열을 합친다. for문 블록에서의 동작도 예외는 아니다.
String result = "";
for (int i = 0; i < 100000; i++) {
result = new StringBuilder().append(result).append("Hello Java").toString();
}
문자열 합치는 코드를 자바 컴파일러가 최적화를 시도하는데 for문에서는 불리하다.
반복 횟수만큼 객체를 생성하고 런타임에 문자열을 연결 갯수와 내용으로 인해 지연된다. 컴파일러는 많은 반복이 일어나는지, 문자열이 어떻게 변하는지 예측할 수 없으므로 최적화가 어려운 부분이다.
String 수동으로 최적화하기
개발자가 직접 개입하여 for문의 연산자(+) 최적화를 개선해주어야 한다.
앞서 설명한 new 키워드로 StringBuilder 객체를 10만번 생성할 필요없이 한번만 이루어주도록 한다.
long startTimer = System.currentTimeMillis();
StringBuilder result = new StringBuilder();
for (int i = 0; i < 100000; i++) {
result.append("Hello Java ");
}
long endTimer = System.currentTimeMillis();
System.out.println("result = " + result);
System.out.println("time = " + (endTimer - startTimer) + "ms");
Java Hello Java
time = 8ms
for 문 블록 밖에서 StringBuilder 객체를 생성하고 append 메소드만 전달하였다.
이로 인해 500배 가까이 개선된 모습을 볼 수 있다.
문자열을 (+) 연산자를 다루는 경우 자바 컴파일러가 알아서 최적화 해주므로 사용해도 되지만, 굉장히 많은 반복문에서는 개발자 개입이 필요하다.
StringBuilder 필요한 상황
StringBuilder 에 대해 몇가지 필요한 상황이 있다.
- 반복해서 문자 연결하는 반복문에서 필요
- 복잡한 문자열을 부분 수정이 필요할 때
- 조건문에서 동적으로 문자열을 조합 로직이 필요할 때
- 대용량 문자열 다룰 때
StringBuffer
StringBuilder 비슷한 클래스로 StringBuffer 클래스가 있다.
StringBuffer 특징 몇 가지를 살펴보자.
- 내부 동기화가 있다.
- 멀티 스레드에서의 상황은 안전하게 동작함.
- 동기화 오버헤드로 인한 성능 저하 있음
StringBulder 클래스에 비해 StringBuffer 클래스를 사용하는 것이 더 좋아보이지만,
StringBuilder 클래스는 동기화 오버헤드가 없으므로 빠른 장점이 있다.