작지만 꾸준한 반복

자바 문자열 덧셈에 대하여 본문

카테고리 없음

자바 문자열 덧셈에 대하여

iamjooon2 2023. 4. 4. 23:38

우테코 문자열 덧셈에 대해 확인하는 수업이 있었다

기존 자동차 경주 코드리뷰에서 String, StringBuilder, StringBuffer에 대해 공부하라는 코멘트도 있어서

성능 차이에 대해서 고민해 볼 수 있는 시간이 이미 있었다.

 

String

- 불변이라 객체 생성 후 상태 변경이 불가능 하다

- String과 String을 더할 경우, 새로운 String을 만든다

-> 즉, 더하는 시점에 메모리 할당 및 해제가 반복되어 단위가 클 경우 성능 이슈 가능성이 있음

 

StringBuilder

- 기존 데이터에 새 데이터를 계속 더하는 방식(append)으로, 속도가 훨씬 빠르다

- 멀티쓰레드 환경 보장하지 않음

 

StringBuffer

- 멀티 쓰레드 상황에서 안전함

- 그만큼 여러 쓰레드에서 접근시 Lock이 걸릴 확률이 있음

 

근데 내가 문자열을 멀티쓰레드 환경에서 사용 할 일이 있을까? 도저히 떠오르지 않는다.

게임 서버..?

아무쪼록 StringBuffer는 배제하였고, StringBuilder와 String + String만 비교하였고

그리고 이 둘의 차이는 10만번 이상의 연산일 때부터 유의미한 차이가 난다고 한다

요런 그래프도 있다

 

아무튼 각설하고, 난 내 눈으로 직접 성능 차이를 보고 싶었다

10만번 이상의 연산시 String 유의미한 차이가 발생한다고 하는데, 진짜 그런지.

 

시간 계산을 도와줄 친구는 현재 시간을 밀리세컨드 단위로 반환하는 currentTimeMillis() 메서드이다.

 

StringBuilder 부터 보자

long start = System.currentTimeMillis();
StringBuilder test = new StringBuilder();
for (int i = 0; i < 100_000; i++) {
    test.append("준희");
}
long end = System.currentTimeMillis();
System.out.println(end - start); // 4

 

String을 확인해보았다

long start = System.currentTimeMillis();
String test = "";
for (int i = 0; i < 100_000; i++) {
    test += "준희";
}
long end = System.currentTimeMillis();
System.out.println(end - start); // 1016

근 200배 차이난다....

 

 

근데 JDK 1.5 이상이면, 컴파일러에서 자동으로 StringBuilder로 변환해 준다고 하며,

그리고 자바 9 부터는 + 연산을 처리할 때, 컴파일러가 동적으로 처리하게 되어 새 객체를 생성하지 않도록 변경되었다고 한다  

 

바이트 코드는 아직 못까겠어서... 더하기 연산은 링크로 대체한다

역시, 내가 일하는 것 보단 JVM이 일하도록 하는게 낫지

결론 

+ 연산은 StringBuilder보다 느린 것은 맞다. 

다만 자바 9부터는 컴파일러에서 + 연산을 StringBuilder로 자동으로 바꿔준다!

 

자바 8 사용하는 곳에서는 StringBuilder 쓰고

자바 9 이상부터는 그냥 + 연산 하자~

 

In the presentation "Enough java.lang.String to Hang Ourselves ...," Dr. Heinz M. Kabutz and Dmitry Vyazelenko discuss the JEP 280-introduced changes to Java string concatenation and summarize it succinctly, "+ is no longer compiled to StringBuilder." In their "Lessons from Today" slide, they state, "Use + instead of StringBuilder where possible" and "recompile classes for Java 9+."

The changes implemented in JDK 9 for JEP 280 "will enable future optimizations of String concatenation without requiring further changes to the bytecode emitted by javac." Interestingly

  • Java 9 이후로 이미 컴파일된 코드라도 JDK 버전 업이 되었을 때 최적화가 되도록 변경된 것으로 보인다.
  • 실제로 Java 11기준 invokedynamic으로 호출되는 StringConcatFactory의 코드를 보면 내부는 StringBuilder를 사용하는 것으로 보인다.

결론 

+ 연산은 StringBuilder보다 느린 것은 맞다. 

다만 자바 9부터는 컴파일러에서 + 연산을 StringBuilder로 자동으로 바꿔준다!

 

한 줄 짜리 연결 코드의 경우 그냥 사용해도 문제가 없지만, 반복하는 횟수가 높아질 수록 StringBuilder 사용을 고려하자!

 

자바 8 사용하는 곳에서는 StringBuilder 쓰고, 자바 9 이상부터는 그냥 + 연산 하자!

 

 

 

 

참고자료

https://hijuworld.tistory.com/2

https://docs.oracle.com/javase/1.5.0/docs/api/java/lang/String.html

https://openjdk.org/jeps/280