본문 바로가기

Programing/JVM(Java, Kotlin)

String, StringBuffer, StringBuilder의 차이점과 장단점?

한성이가 StringBuffer와 StringBuilder의 차이에 대해서 물어봐서 ThreadSafe 한지 안한지 차이라고 확인해주었다.

그런데 인터넷에 "String, StringBuffer, StringBuilder의 차이점과 장단점은 뭔가요?"를 글을 보다가 미물님이 테스트한 글을 보게 되었다.


테스트의 요지는 StringBuffer랑 문자열을 +로 붙이는 것의 비교였다.

왠지 컴파일 될 때 최적화가 되어 문자열이 하나로 합쳐지는 것이 아닌가 의심이 되었다.


테스트 JDK는 1.7.0_45이다.

그래서 아래와 같은 간단한 클래스를 만들어보았다.

class Test

{

       public static void main(String[] args)

       {

             String str1 = "Hello";

             String str2 = "World!";

             String str = str1 + " " + str2;

             System.out.println(str);

       }

}


str이 "Hello World!" 와 같은 형태로 저장이 된다면 최적화를 할 수 있다고 볼 수도 있는 것이다.


아래와 같은 일련의 단계를 걸쳐 바이트코드를 얻을 수 있었다.

Test.java --- javac.exe -> Test.class  --- javap Test.class -> 바이트코드


> javac Test.java

> java Test

Hello World!

> javap -v -p Test.class > bytecode.txt

결과: bytecode.txt


생각과는 달리 코드에서 문자열은 합쳐져서 존재하지 않았고 각각의 문자열 풀에 존재하였다.

또한 + 연산은 StringBuilder에 의해 append 되고 있었다.

 0 ldc #2 <Hello>

 2 astore_1

 3 ldc #3 <World!>

 5 astore_2

 6 new #4 <java/lang/StringBuilder>

 9 dup

10 invokespecial #5 <java/lang/StringBuilder.<init>>

13 aload_1

14 invokevirtual #6 <java/lang/StringBuilder.append>

17 ldc #7 < >

19 invokevirtual #6 <java/lang/StringBuilder.append>

22 aload_2

23 invokevirtual #6 <java/lang/StringBuilder.append>

26 invokevirtual #8 <java/lang/StringBuilder.toString>

29 astore_3

30 getstatic #9 <java/lang/System.out>

33 aload_3

34 invokevirtual #10 <java/io/PrintStream.println>

37 return


그렇다면 메서드를 호출하는 경우에는 동일할까?

class Test

{

       private static String operation()

       {

             String str = "Hello" + " " + "World!";

             return str;

       }

 

       private static String stringBuilder()

       {

             StringBuilder sb = new StringBuilder("Hello");

             sb.append(" ");

             sb.append("World!");

             String str = sb.toString();

             return str;

       }

 

       public static void main(String[] args)

       {

             System.out.println(operation());

             System.out.println(stringBuilder());

       }

}


바이트 코드를 보면 

operation메서드에서 문자열을 붙이지 않고 아예 상수로 가지고 있음을 알 수 있다.

0 ldc #2 <Hello World!>

2 astore_0

3 aload_0

4 areturn


반면에 stringBuilder 메서드는 연산을 수행한다.
 0 new #3 <java/lang/StringBuilder>
 3 dup
 4 ldc #4 <Hello>
 6 invokespecial #5 <java/lang/StringBuilder.<init>>
 9 astore_0
10 aload_0
11 ldc #6 < >
13 invokevirtual #7 <java/lang/StringBuilder.append>
16 pop
17 aload_0
18 ldc #8 <World!>
20 invokevirtual #7 <java/lang/StringBuilder.append>
23 pop
24 aload_0
25 invokevirtual #9 <java/lang/StringBuilder.toString>
28 astore_1
29 aload_1
30 areturn

따라서 최적화 되어 문자열 연산을 하지 않기에 빠를 수도 있는 것이다.


마지막으로 미물님이 테스트한 코드의 바이트 코드를 살펴보았다.

private static Repeatable stringConcatenationTest = new Repeatable()

    {

        public void iteration(long loopId) {

            String query =

                "SELECT " +

                    "a.userName, " +

                    "a.registerDate, " +

                    "sum(a.duration), " +

                    "a.id " +

                "FROM " +

                 StringTest.class.getName() +

                    " as a " + "ORDER BY " +

                    "a.registerDate desc";

            String result = query;

        }

    };

stringConcatenationTest 필드의 경우 아래와 같이 바이트코드로 된다.

 0 new #2 <java/lang/StringBuilder>

 3 dup

 4 invokespecial #3 <java/lang/StringBuilder.<init>>

 7 ldc #4 <SELECT a.userName, a.registerDate, sum(a.duration), a.id FROM >

 9 invokevirtual #5 <java/lang/StringBuilder.append>

12 ldc_w #6 <Test>

15 invokevirtual #7 <java/lang/Class.getName>

18 invokevirtual #5 <java/lang/StringBuilder.append>

21 ldc #8 < as a >

23 invokevirtual #5 <java/lang/StringBuilder.append>

26 ldc #9 <ORDER BY >

28 invokevirtual #5 <java/lang/StringBuilder.append>

31 ldc #10 <a.registerDate desc>

33 invokevirtual #5 <java/lang/StringBuilder.append>

36 invokevirtual #11 <java/lang/StringBuilder.toString>

39 astore_3

40 aload_3

41 astore 4

43 return



추과 관련 링크: http://www.slipp.net/questions/271