JAVA/고급 자바

문자열 연결 시 실행되는 내부 로직

반응형

문자열 생성

String 문자열을 연결할 때 아래와 같이 객체로써 선언해서 사용할 경우 불필요한 객체가 만들어진다는걸 우리는 알고있다.

String temp = new String("wedul");

그래서 String을 아래와 같이 선언해서 String constants Pool에 저장해서 불변객체를 생성해서 동일한 문자열에 대해서 새로 생성하지 않고 가져다 사용할 수 있도록 할수있다. (effective java. 불필요한 객체를 만들지 마라.)

String temp = "wedul"

 

문자열 연결

문자열 생성 시 우리는 + 로 문자열을 연결할 경우 새로운 문자열 객체가 계속 생성되기 때문에 이를 해결하기 위해서 StringBuilder를 통해서 모든 append가 끝나고 build를 통해 문자열 객체를 한번만 생성하라고 배웠었다. 

 

하지만 이 부분은 jdk 1.5 버전 부터는 컴파일 시에 자동으로 +를 사용해도 StringBuilder를 사용하는 형태로 바꿔주기 때문에 굳이 문자열을 concat할 때 StringBuilder를 사용하지 않아도 되었다.

ex)

java.lang.String concat(java.lang.String, int);
  Code:
     0: new           #2      // class StringBuilder
     3: dup
     4: invokespecial #3      // Method StringBuilder."<init>":()V
     7: aload_0
     8: invokevirtual #4      // Method StringBuilder.append:(LString;)LStringBuilder;
    11: iload_1
    12: invokevirtual #5      // Method StringBuilder.append:(I)LStringBuilder;
    15: invokevirtual #6      // Method StringBuilder.toString:()LString;

 

하지만 +의 형태를 StringBuilder의 형태로 변경을 해주다 보니 loop의 상황에서는 아래와 같이 계속해서 StringBuilder객체가 생성되는 문제가 발생한다.

// 작성한 코드
String temp = "";

for (int i = 0; i < 10; i++) {
	temp += i; 
}

// 디컴파일 코드
String temp = "";

for (int i = 0; i < 10; i++) {
	temp = (new StringBuilder()).append(i).toString();
}

 

 

이런 문제를 해결하기 위해 jdk 1.9버전 부터는 StringBuilder가 아닌 makeConcatWithConstants을 사용하여 문자열을 붙이도록 변경되었다.

https://www.baeldung.com/java-string-concatenation-invoke-dynamic

 

String Concatenation with Invoke Dynamic | Baeldung

Learn about relatively new Java optimization: string concatenation with invokedynamic

www.baeldung.com

https://docs.oracle.com/javase/9/docs/api/java/lang/invoke/StringConcatFactory.html

 

StringConcatFactory (Java SE 9 & JDK 9 )

Facilitates the creation of optimized String concatenation methods, that can be used to efficiently concatenate a known number of arguments of known types, possibly after type adaptation and partial evaluation of arguments. Typically used as a bootstrap me

docs.oracle.com

Warning: File ./Test.class does not contain class Test
Classfile /Users/wedul/Documents/project/algorizmtest/src/tt/Test.class
  Last modified 2022. 10. 31.; size 1087 bytes
  SHA-256 checksum 9a1511286f28216ac376bebae126abd8f0d64b48ddc22d00d54c91c07dd0e25c
  Compiled from "Test.java"
public class tt.Test
  minor version: 0
  major version: 61
  flags: (0x0021) ACC_PUBLIC, ACC_SUPER
  this_class: #10                         // tt/Test
  super_class: #2                         // java/lang/Object
  interfaces: 0, fields: 0, methods: 3, attributes: 3
Constant pool:
   #1 = Methodref          #2.#3          // java/lang/Object."<init>":()V
   #2 = Class              #4             // java/lang/Object
   #3 = NameAndType        #5:#6          // "<init>":()V
   #4 = Utf8               java/lang/Object
   #5 = Utf8               <init>
   #6 = Utf8               ()V
   #7 = String             #8             // test
   #8 = Utf8               test
   #9 = Methodref          #10.#11        // tt/Test.reversString:(Ljava/lang/String;)Ljava/lang/String;
  #10 = Class              #12            // tt/Test
  #11 = NameAndType        #13:#14        // reversString:(Ljava/lang/String;)Ljava/lang/String;
  #12 = Utf8               tt/Test
  #13 = Utf8               reversString
  #14 = Utf8               (Ljava/lang/String;)Ljava/lang/String;
  #15 = Fieldref           #16.#17        // java/lang/System.out:Ljava/io/PrintStream;
  #16 = Class              #18            // java/lang/System
  #17 = NameAndType        #19:#20        // out:Ljava/io/PrintStream;
  #18 = Utf8               java/lang/System
  #19 = Utf8               out
  #20 = Utf8               Ljava/io/PrintStream;
  #21 = Methodref          #22.#23        // java/io/PrintStream.println:(Ljava/lang/String;)V
  #22 = Class              #24            // java/io/PrintStream
  #23 = NameAndType        #25:#26        // println:(Ljava/lang/String;)V
  #24 = Utf8               java/io/PrintStream
  #25 = Utf8               println
  #26 = Utf8               (Ljava/lang/String;)V
  #27 = Class              #28            // java/lang/String
  #28 = Utf8               java/lang/String
  #29 = String             #30            // wedul
  #30 = Utf8               wedul
  #31 = Methodref          #27.#32        // java/lang/String."<init>":(Ljava/lang/String;)V
  #32 = NameAndType        #5:#26         // "<init>":(Ljava/lang/String;)V
  #33 = String             #34            //
  #34 = Utf8
  #35 = Methodref          #27.#36        // java/lang/String.toCharArray:()[C
  #36 = NameAndType        #37:#38        // toCharArray:()[C
  #37 = Utf8               toCharArray
  #38 = Utf8               ()[C
  #39 = InvokeDynamic      #0:#40         // #0:makeConcatWithConstants:(Ljava/lang/String;)Ljava/lang/String;
  #40 = NameAndType        #41:#14        // makeConcatWithConstants:(Ljava/lang/String;)Ljava/lang/String;
  #41 = Utf8               makeConcatWithConstants
  #42 = Utf8               Code
  #43 = Utf8               LineNumberTable
  #44 = Utf8               main
  #45 = Utf8               ([Ljava/lang/String;)V
  #46 = Utf8               SourceFile
  #47 = Utf8               Test.java
  #48 = Utf8               BootstrapMethods
  #49 = MethodHandle       6:#50          // REF_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;
  #50 = Methodref          #51.#52        // 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;
  #51 = Class              #53            // java/lang/invoke/StringConcatFactory
  #52 = NameAndType        #41:#54        // makeConcatWithConstants:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/String;[Ljava/lang/Object;)Ljava/lang/invoke/CallSite;
  #53 = Utf8               java/lang/invoke/StringConcatFactory
  #54 = Utf8               (Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/String;[Ljava/lang/Object;)Ljava/lang/invoke/CallSite;
  #55 = String             #56            // \u0001111
  #56 = Utf8               \u0001111
  #57 = Utf8               InnerClasses
  #58 = Class              #59            // java/lang/invoke/MethodHandles$Lookup
  #59 = Utf8               java/lang/invoke/MethodHandles$Lookup
  #60 = Class              #61            // java/lang/invoke/MethodHandles
  #61 = Utf8               java/lang/invoke/MethodHandles
  #62 = Utf8               Lookup
{
  public tt.Test();
    descriptor: ()V
    flags: (0x0001) ACC_PUBLIC
    Code:
      stack=1, locals=1, args_size=1
         0: aload_0
         1: invokespecial #1                  // Method java/lang/Object."<init>":()V
         4: return
      LineNumberTable:
        line 3: 0

  public static void main(java.lang.String[]);
    descriptor: ([Ljava/lang/String;)V
    flags: (0x0009) ACC_PUBLIC, ACC_STATIC
    Code:
      stack=3, locals=3, args_size=1
         0: ldc           #7                  // String test
         2: invokestatic  #9                  // Method reversString:(Ljava/lang/String;)Ljava/lang/String;
         5: astore_1
         6: getstatic     #15                 // Field java/lang/System.out:Ljava/io/PrintStream;
         9: aload_1
        10: invokevirtual #21                 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
        13: new           #27                 // class java/lang/String
        16: dup
        17: ldc           #29                 // String wedul
        19: invokespecial #31                 // Method java/lang/String."<init>":(Ljava/lang/String;)V
        22: astore_2
        23: return
      LineNumberTable:
        line 6: 0
        line 7: 6
        line 8: 13
        line 9: 23

  public static java.lang.String reversString(java.lang.String);
    descriptor: (Ljava/lang/String;)Ljava/lang/String;
    flags: (0x0009) ACC_PUBLIC, ACC_STATIC
    Code:
      stack=1, locals=3, args_size=1
         0: ldc           #33                 // String
         2: astore_1
         3: aload_0
         4: invokevirtual #35                 // Method java/lang/String.toCharArray:()[C
         7: astore_2
         8: aload_1
         9: invokedynamic #39,  0             // InvokeDynamic #0:makeConcatWithConstants:(Ljava/lang/String;)Ljava/lang/String;
        14: astore_1
        15: aload_1
        16: invokedynamic #39,  0             // InvokeDynamic #0:makeConcatWithConstants:(Ljava/lang/String;)Ljava/lang/String;
        21: astore_1
        22: aload_1
        23: invokedynamic #39,  0             // InvokeDynamic #0:makeConcatWithConstants:(Ljava/lang/String;)Ljava/lang/String;
        28: astore_1
        29: aload_1
        30: invokedynamic #39,  0             // InvokeDynamic #0:makeConcatWithConstants:(Ljava/lang/String;)Ljava/lang/String;
        35: astore_1
        36: aload_1
        37: areturn
      LineNumberTable:
        line 12: 0
        line 13: 3
        line 15: 8
        line 16: 15
        line 17: 22
        line 18: 29
        line 23: 36
}
SourceFile: "Test.java"
BootstrapMethods:
  0: #49 REF_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;
    Method arguments:
      #55 \u0001111
InnerClasses:
  public static final #62= #58 of #60;    // Lookup=class java/lang/invoke/MethodHandles$Lookup of class java/lang/invoke/MethodHandles
반응형