버릇처럼 StringBuilder 를 생성해서 필요한 문자열을 만들고 있었는데, IntelliJ에서 경고를 해왔다.
왜? String 을 + 연산 하면 메모리에 낭비가 생기는 거 아니었어?
String 의 + 연산과 StringBuilder의 성능 차이에 대해 서칭하다가 알게 된 사실
public String plusOperate() {
String result = "first";
result += "second";
result += "third";
result += "forth";
result += "fifth";
return result;
}
String 의 + 연산은 bytecode에서 자동으로 StringBuilder init, append로 변경돼서 호출된다.
Amazon Corretto 17 의 ByteCode 결과는 또 달랐다.
LINENUMBER 6 L1
ALOAD 1
INVOKEDYNAMIC makeConcatWithConstants(Ljava/lang/String;)Ljava/lang/String; [
// handle kind 0x6 : INVOKESTATIC
java/lang/invoke/StringConcatFactory.makeConcatWithConstants(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/String;[Ljava/lang/Object;)Ljava/lang/invoke/CallSite;
// arguments:
"\u0001second"
]
StringConcatFactory 의 makeConcatWithConstants 메소드를 호출한다.
예상과 다른 결과에 다시 검색..
jdk 9 버젼부터 StringBuilder 혹은 StringBuffer 를 이용한 String 의 연산을 사용하지 않는다.
그이유는
1) StringBuilder 혹은 StringBuffer 는 별도로 할당하지 않으면 16 characters 의 크기를 할당받아 메모리 재할당으로 인한 추가 비용이 발생하기 쉬웠고,
2) loop 안에서의 호출 시 반복되는 객체의 생성이 발생한다.
확인해보니 정말 temurin 1.8 바이트코드는 결과는
LINENUMBER 8 L1
NEW java/lang/StringBuilder
DUP
INVOKESPECIAL java/lang/StringBuilder.<init> ()V
ALOAD 1
INVOKEVIRTUAL java/lang/StringBuilder.append (Ljava/lang/String;)Ljava/lang/StringBuilder;
LDC "second"
INVOKEVIRTUAL java/lang/StringBuilder.append (Ljava/lang/String;)Ljava/lang/StringBuilder;
INVOKEVIRTUAL java/lang/StringBuilder.toString ()Ljava/lang/String;
ASTORE 1
public String forLoopPlus() {
String result = "";
for(int index = 0; index < 100; ++index) {
result = result + index;
}
return result;
}
L2
FRAME APPEND [java/lang/String I]
ILOAD 2
BIPUSH 100
IF_ICMPGE L3
L4
LINENUMBER 24 L4
ALOAD 1
ILOAD 2
INVOKEDYNAMIC makeConcatWithConstants(Ljava/lang/String;I)Ljava/lang/String; [
// handle kind 0x6 : INVOKESTATIC
java/lang/invoke/StringConcatFactory.makeConcatWithConstants(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/String;[Ljava/lang/Object;)Ljava/lang/invoke/CallSite;
// arguments:
"\u0001\u0001"
]
ASTORE 1
L5
LINENUMBER 23 L5
IINC 2 1
GOTO L2
L2
FRAME APPEND [java/lang/String I]
ILOAD 2
BIPUSH 100
IF_ICMPGE L3
L4
LINENUMBER 18 L4
NEW java/lang/StringBuilder
DUP
INVOKESPECIAL java/lang/StringBuilder.<init> ()V
ALOAD 1
INVOKEVIRTUAL java/lang/StringBuilder.append (Ljava/lang/String;)Ljava/lang/StringBuilder;
ILOAD 2
INVOKEVIRTUAL java/lang/StringBuilder.append (I)Ljava/lang/StringBuilder;
INVOKEVIRTUAL java/lang/StringBuilder.toString ()Ljava/lang/String;
ASTORE 1
L5
LINENUMBER 17 L5
IINC 2 1
GOTO L2
정말 계속해서 StringBuilder 가 init된다..
for 와 같은 결과.
https://jerry92k.tistory.com/50
https://dzone.com/articles/string-concatenation-performacne-improvement-in-ja
https://choichumji.tistory.com/135