Skip to content

Instantly share code, notes, and snippets.

@benelog
Last active April 14, 2023 13:42
Show Gist options
  • Star 9 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save benelog/b81b4434fb8f2220cd0e900be1634753 to your computer and use it in GitHub Desktop.
Save benelog/b81b4434fb8f2220cd0e900be1634753 to your computer and use it in GitHub Desktop.
String 최적화 JDK 1.5

jdk1.5에서 String 더하기의 컴파일시의 최적화]

String 클래스를 "+"로 반복해서 더하는 연산을 어떤 경우에 컴파일러에서 자동으로 최적화해주는지 알고 있다면 보다 융퉁성 있게 쓸 수가 있습니다.

몇년전에 javaservice.net에서의 String vs StringBuffer에 대한 논의에서도 이 이야기가 오고 갔었고, 그를 통해 제가 알게 되었던 바는 다음과 같습니다.

  1. 한줄에서 상수 String끼리만 더하는 것은 모두 합쳐진 문자열로 바꿔준다. 즉 String a= "a" + "b" + "c"; 라고 쓰면 String ="abc"; 로 알아서 컴파일해준다는 거죠.
  2. 한줄에서 상수와 다른 String 클래스를 더하는 것은 StringBuffer의 append, toString 메서드를 쓰는 코드로 준다. jdk 1.4 javadoc 의 StringBuffer API설명 에 명시되어 있네요.

String buffers are used by the compiler to implement the binary string concatenation operator . For example, the code:
x = "a" + 4 + "c"
is compiled to the equivalent of:
x = new StringBuffer().append("a").append(4).append("c").toString()
which creates a new string buffer (initially empty), appends the string representation of each operand to the string buffer in turn, and then converts the contents of the string buffer to a string. Overall, this avoids creating many temporary strings.

Java 1.5 이상에서는 String더하기가 StringBuilder로 치환된다는 것을 듣고나서, 이것을 직접 테스트 해보았습니다. jdk1.5의 API문서를 보시면 아시겠지만 StringBuilder는 동기화되지 않았다는 것이 SringBuffer와 차이점입니다.

참고로 컴파일은 이클립스에서 한 후 jad로 다시 역컴파일한 결과입니다.

원래 소스

public class StringTest {  
    public static void main(String[] args) {  
        String str0 = "It's a string....";  
        String str1 = "It's" + " a string" + "....";  
        String str2 = "It's a string...." + str0 + "000";  
        str2 = str0 + str1 + "1111" ;        
        str2 = str2 + "1111";  
        str2 += "1111";        
        for (int i=0;i<10;i++){  
            str2 = str2 + "1111";  
            str2 += "1111";        
        }  
    }  
}

jdk 1.4로 compile

public class StringTest{
 
    public StringTest()    {  
    }
 
    public static void main(String args[])    {  
        String str0 = "It's a string....";  
        String str1 = "It's a string....";  
        String str2 = "It's a string...." + str0 + "000";  
        str2 = str0 + str1 + "1111";  
        str2 = str2 + "1111";  
        str2 = str2 + "1111";  
        for(int i = 0; i < 10; i++)        {  
            str2 = str2 + "1111";  
            str2 = str2 + "1111";  
        }

    }  
} 

JDK 1.5로 compile

public class StringTest{  
    public StringTest()    {  
    }

    public static void main(String args[])    {  
        String str0 = "It's a string....";  
        String str1 = "It's a string....";  
        String str2 = (new StringBuilder("It's a string....")).append(str0).append("000").toString();  
        str2 = (new StringBuilder(String.valueOf(str0))).append(str1).append("1111").toString();  
        str2 = (new StringBuilder(String.valueOf(str2))).append("1111").toString();  
        str2 = (new StringBuilder(String.valueOf(str2))).append("1111").toString();  
        for(int i = 0; i < 10; i++)        {  
            str2 = (new StringBuilder(String.valueOf(str2))).append("1111").toString();  
            str2 = (new StringBuilder(String.valueOf(str2))).append("1111").toString();  
        }  
    }  
}

상수 더하기는 역시 String str1 = "It's" + " a string" + "...."; -> String str1 = "It's a string...."; 으로 양 버전 모두에서 바뀝니다. 상수와 상수가 아닌 것을 섞어서 더했는 때는 jdk1.4로 이클립스에서 컴파일한 결과로는 StringBuffer가 나타나지는 않네요. 그리고 1.5에서는 예상대로 StringBuilder가 나타납니다. jdk1.4에서 StringBuffer로 자동치환이 안되어서 나오는 것은 좀 이상하기는 해도, 반복문이 아닌 곳에서 스트링 한두개를 더하는 정도라면 최적화해 주지 않아도 한 두개의 객체가 더 생기는 정도일 것이니까 큰 성능의 차이는 없을 것 같습니다.

1.5에서는 반복문 안에서의 더하기도 StringBuilder로 바꿔주기는 하지만 매루프마다 새로운 StringBuilder 클래스를 생성하는 것이므로 String과 마찬가지로 필요없는 임시객체를 계속 만들게 됩니다. 즉 어떤 경우라도 반복문안에서 String 더하기에 "+"를 쓰지는 말아야 겠죠.

@benelog
Copy link
Author

benelog commented Aug 9, 2021

@apollyon4
더 정확히 표현하면, '코드를 원복하는 기능' 이라기보다는, 바이트코드를 그대로 옮기지 않고 원래 코드일것으로 '추정'되는 모습으로 디컴파일을 해준다고 봐야할것 같습니다. 바이트코드에 소스코드의 정보가 100%는 없으니 이 추정이 완전히 맞지 않는 경우도 있을듯합니다.

테스트해보진 않았지만, 소스에서 StringBuilder를 쓴 코드도 "+"을 썼던 소스 코드로 추정해서 역컴파일하는 경우도 디컴파일의 구현와 옵션에 따라서는 발생할수 있을듯합니다.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment