티스토리 뷰

728x90
반응형

문자열 연결은 느리니 주의하라


  • 문자열 연결 연산자(+)는 여러 문자열을 하나로 합쳐주는 편리한 수단입니다. 하지만 여러 문자열을 많이 합치게 되는 경우 성능 저하의 문제가 발생할 수 있습니다.
  • 문자열 연결 연산자(+)로 문자열 n개를 잇는 시간은 n2에 비례합니다.

 

💡연결 연산자를 사용하는 경우

public class Example {

    public static void main(String[] args) {

        String str = "";

        for (int i = 0; i < 100000; i++) {
            str += i;
        }

        System.out.println(str); // 16 sec, 948ms 소요
    }
}

 

💡StringBuilder를 사용하는 경우

  • 상당히 빨라진것을 알 수 있습니다.
public class Example {

    public static void main(String[] args) {

        StringBuilder sb = new StringBuilder();

        for (int i = 0; i < 100000; i++) {
            sb.append(i);
        }

        System.out.println(sb.toString()); // 2 sec, 803ms 소요
    }
}

 

 

💡String, StringBuffer, StringBuilder

  • String과 StringBuffer는 자바 1.0의 등장과 함께 나왔습니다.
  • StringBuilder는 자바 1.5에서 나왔습니다.
  • String의 concat연산은 + 기호를 사용하여 concatination을 수행합니다.
  • StringBuffer와 StringBuilder는 AbstractStringBuilder를 사용하고 있으며, 결국 같은 append 메서드를 사용합니다.

 

💡StringBuffer, StringBuilder의 차이

  • StringBuffer의 append 메서드에는 syncronized 키워드가 있어서 thread-safe합니다. 하지만 StringBuilder의 append 메서드에는 해당 키워드가 없어 thread-safe하지 않습니다.
// StringBuffer
@Override
@HotSpotIntrinsicCandidate
public synchronized StringBuffer append(String str) {
    toStringCache = null;
    super.append(str);
    return this;
}
    
// StringBuilder
@Override
@HotSpotIntrinsicCandidate
public StringBuilder append(String str) {
    super.append(str);
    return this;
}

 

  • 멀티 쓰레드 환경에서는 StringBuffer를 사용하는것이 안전합니다.
  • 단일 thread라면 StringBuilder를 사용하는것이 StringBuffer를 사용하는 것 보다 성능이 좋습니다.

 

💡String의 + 연산이 느린 이유

  • String은 불변 클래스이기 때문에 String + String을 하기 위해서는 String 내의 char[] 또는 byte[]을 복사합니다. 
  • 2개의 array의 length를 더한 값으로 새로운 배열을 만들게 됩니다. 그리고 array에 기존의 값을 채워넣고 new String으로 새로운 String 객체를 생성하게 됩니다. 이렇게 + 연산이 일어날 때마다 새로운 객체가 생성되니 힙 영역에서 String 객체가 많아지면 GC가 String 객체를 수거하기 시작하는데 GC동작시 stop the word라는 행위가 일어나게 됩니다.(JVM의 작동이 일시적으로 멈춥니다.)
    이러한 행위가 계속 반복되면 결국 느려질 수 밖에 없습니다.

 

💡String의 + 연산자의 발전

  • Java String 연산에 대한 성능 최적화를 다방면으로 생각하고 Java 9부터는 String의 내부 배열을 char[] -> byte[]로 변경하여 성능을 더 향상 시켰습니다.
  • java 1.5 버전부터 String + String 연산에 대해 컴파일 타임에 StringBuilder를 사용하도록 코드를 변경합니다. 하지만 JDK가 항상 자동으로 바꿔준다는 보장이 없기 때문에 해당 연산에 대해서는 안전하게 StringBuilder를 사용하는게 좋습니다.

 

 

참고 자료)

https://jaehun2841.github.io/2019/03/01/effective-java-item63/#StringBuilder-append-%EB%A9%94%EC%84%9C%EB%93%9C-%ED%8C%8C%ED%97%A4%EC%B9%98%EA%B8%B0

 

 

 

 

 

 

728x90
반응형