StringBuffer의 초기화(Clear)의 3가지 방법 비교

    데이터 분석을 많이 하거나, 대용량의 텍스트를 저장하기에 상당히 유용한 객체로 StringBuffer가 있다. 값을 한번에 저장하는 방법은 String으로 선언된 변수에 한번에 값을 넣는 방법이겠지만, 사이즈가 좀 큰 문자열은 한번에 넣기가 쉽지가 않다. 예를 들어 csv를 만든다고 할 때 그때그때 bw.write로 파일을 내리는 것보다 StringBuffer에 일정만큼 저장한 후 한번에 bw.write에 값을 전달하는 것이 훨씬 속도면에서 유리하다.

     

    그러나 StringBuffer에서 데이터를 새로 담을 때, 아무 생각없이 new StringBuffer()로 계속된 초기화를 한적이 있는데 우연찮게 찾아본 문서를 보고 이 부분에 대해서 좀 더 고민할 필요가 있을 것 같아 포스팅을 공유해보고자 한다.

     

    StringBuffer 초기화 3가지 방법

     

    new StringBuffer() 방식

    우선 남들은 보편적으로 사용하고 있는지 모르겠으나, 나는 이 방식을 애용 했었다. GC야 튜닝을 워낙 잘 해서인지 CPU Load가 튀는 적도 없었고 대용량 서비스를 런칭해도 아무런 문제가 없었으나, 돌다리도 두들겨보라고 앞으로 사용을 자제하거나 API 서비스가 아닌 Back단에서 Analyze 하는 쪽에서나 사용하는 것이 좋을 것 같다. 

     

    package com.tistory.needjarvis;
    
    public class Main {
    
        public static void main(String[] args) {
            StringBuffer sb = new StringBuffer();
            for(int i = 0; i < 1000000; i++) {
                sb.append("문자열 ").append(i);
            }
            System.out.println("string buffer 사이즈 -> " + sb.length());
            System.out.println("string buffer Clear");
            long startTime = System.currentTimeMillis();
            sb = new StringBuffer();
            sb.append("새로운 결과");
            System.out.println("string buffer 사이즈 -> " + sb.length());
            System.out.println("elapsed -> " + (System.currentTimeMillis() - startTime) +"(ms)");
        }
    }
    
    
    # 실행결과
    string buffer 사이즈 -> 9888890
    string buffer Clear
    string buffer 사이즈 -> 6 새로운 결과
    elapsed -> 0(ms)

    아무래도 새로 초기화를 하다보니 다른 방식보다 심플하고 가장 빠를 것 같다 생각하고 있었는데 해외 프로그램 사이트에서는 다음과 같은 것을 경고하고 있다.

     

    Here, new StringBuffer() creates a new string buffer object and assigns the previous variable to the new objects. In this case, the previous object will be there. But it won't be accessible so it will be garbage collected.
    Since, every time instead of clearing the previous string buffer, a new string buffer is created. So it is less efficient in terms of performance.

    위 내용을 보면 새 개체를 만들게 되면서 이전 개체가 존재하게 되며, 이를 garbase collected 즉 GC가 실행되어 수거하게 된다고 나온다. 만약 메모리를 많이 사용하는 경우 GC가 빈번히 발생할 수 있다는 점이며, 성능 측면에서도 좋지 않다 한다. 즉, 한마디로 사용을 자제하라는 것이다.

     

     

    setLength로 초기화

    StringBuffer의 Length를 0으로 해서 초기화를 하는 방법도 있다.

    package com.tistory.needjarvis;
    
    public class Main {
    
        public static void main(String[] args) {
            StringBuffer sb = new StringBuffer();
            for(int i = 0; i < 1000000; i++) {
                sb.append("문자열 ").append(i);
            }
            System.out.println("string buffer 사이즈 -> " + sb.length());
            System.out.println("string buffer Clear");
            long startTime = System.currentTimeMillis();
            sb.setLength(0);
            System.out.println("string buffer 사이즈 -> " + sb.length());
            System.out.println("elapsed -> " + (System.currentTimeMillis() - startTime) +"(ms)");
        }
    }
    
    
    # 실행결과
    string buffer 사이즈 -> 9888890
    string buffer Clear
    string buffer 사이즈 -> 6 새로운 결과
    elapsed -> 0(ms)

    length를 조절해서도 이렇게 쉽게 클리어를 해준다.

     

    Here, the setLength() method changes the character sequences present in StringBuffer to a new character sequence. And, set the length of the new character sequence to 0.
    Hence, the older character sequence is garbage collected.

    다만 이 방법도 만능은 아니라 이전 문자는 garbage collected 된다고 나와 있다. 다만 방식이 심플해서 새로운 객체를 생성하는 new StringBuffer보다 효율적 인것으로 보이며, 코드 라인도 심플해진다.

     

    Delete() 사용

    package com.tistory.needjarvis;
    
    public class Main {
    
        public static void main(String[] args) {
            StringBuffer sb = new StringBuffer();
            for(int i = 0; i < 1000000; i++) {
                sb.append("문자열 ").append(i);
            }
            System.out.println("string buffer 사이즈 -> " + sb.length());
            System.out.println("string buffer Clear");
            long startTime = System.currentTimeMillis();
            sb.delete(0, sb.length());
            sb.append("새로운 결과");
            System.out.println("string buffer 사이즈 -> " + sb.length() + " " + sb.toString());
            System.out.println("elapsed -> " + (System.currentTimeMillis() - startTime) +"(ms)");
        }
    }
    
    # 실행결과
    string buffer 사이즈 -> 9888890
    string buffer Clear
    string buffer 사이즈 -> 6 새로운 결과
    elapsed -> 0(ms)

    그리고 대망의 Delete 방법... 이 방식은 장단점이 명확히 나뉘어지지만 솔직히 내부적으로 테스트 해봤을 때는 장점만 있는 것 같다.

    In the above example, we have used the delete() method of the StringBuffer class to clear the string buffer.
    Here, the delete() method removes all the characters within the specified index numbers.

    일단 설명을 보면 GC가 발동되지 않는 것을 알 수 있다. 이전 데이터를 메모리에 저장하지 않고 명확하게 "삭제"하는 방식이라 GC가 발생되지 않는 것으로 파악된다. 결국 API와 같은 서비스에서 일정한 속도가 중요한 경우 GC를 최소화 시키는 것이 중요한데 그럴때에는 new StringBuffer와 같이 GC를 유도하는 방식보다 delete로 GC 자체를 호출하지 않는 방식이 훨씬 효율적인 것 같다.

     

    물론 후자의 방식은 어느정도 CPU를 사용하겠지만, Java는 GC만 뜨지 않는다면 API와 같은 서비스에서는 무조건 GC가 최소화하는 것이 좋을 것이다.

     

     

    참고자료

    https://www.programiz.com/java-programming/examples/clear-stringbuffer

    댓글

    Designed by JB FACTORY