[java] Java 9에서 문자열 연결은 어떻게 구현됩니까?

JEP 280에 쓰여진대로 : Indify String Concatenation :

JDK 라이브러리 함수에 대한 호출을 사용하기 위해에서 String생성 된 정적 연결 바이트 코드 시퀀스를 변경합니다 . 이렇게하면에 의해 방출 된 바이트 코드를 추가로 변경하지 않고도 향후 연결 최적화가 가능해집니다 .javacinvokedynamicStringjavac

여기에서 invokedynamic호출 의 사용이 무엇 이며 바이트 코드 연결이 어떻게 다른지 이해하고 싶습니다 invokedynamic.



답변

“오래된”방식은 StringBuilder일련 의 지향적 인 작업을 출력 합니다. 이 프로그램을 고려하십시오.

public class Example {
    public static void main(String[] args)
    {
        String result = args[0] + "-" + args[1] + "-" + args[2];
        System.out.println(result);
    }
}

JDK 8 또는 이전 버전으로 컴파일 한 다음 사용 javap -c Example하여 바이트 코드를 보면 다음과 같은 내용이 표시됩니다.

공용 클래스 예 {
  공개 예제 ();
    암호:
       0 : aload_0
       1 : invokespecial # 1 // 메소드 java / lang / Object. "<init>":() V
       4 : 반환

  public static void main (java.lang.String []);
    암호:
       0 : 새로운 # 2 // 클래스 java / lang / StringBuilder
       3 : 복제
       4 : invokespecial # 3 // 메소드 java / lang / StringBuilder. "<init>":() V
       7 : aload_0
       8 : 아이콘 t_0
       9 : aaload
      10 : invokevirtual # 4 // 메소드 java / lang / StringBuilder.append : (Ljava / lang / String;) Ljava / lang / StringBuilder;
      13 : ldc # 5 // 문자열-
      15 : invokevirtual # 4 // 메소드 java / lang / StringBuilder.append : (Ljava / lang / String;) Ljava / lang / StringBuilder;
      18 : aload_0
      19 : iconst_1
      20 : aaload
      21 : invokevirtual # 4 // 메소드 java / lang / StringBuilder.append : (Ljava / lang / String;) Ljava / lang / StringBuilder;
      24 : ldc # 5 // 문자열-
      26 : invokevirtual # 4 // 메소드 java / lang / StringBuilder.append : (Ljava / lang / String;) Ljava / lang / StringBuilder;
      29 : aload_0
      30 : iconst_2
      31 : aaload
      32 : invokevirtual # 4 // 메소드 java / lang / StringBuilder.append : (Ljava / lang / String;) Ljava / lang / StringBuilder;
      35 : invokevirtual # 6 // 메소드 java / lang / StringBuilder.toString :() Ljava / lang / String;
      38 : astore_1
      39 : getstatic # 7 // 필드 java / lang / System.out : Ljava / io / PrintStream;
      42 : aload_1
      43 : invokevirtual # 8 // 메소드 java / io / PrintStream.println : (Ljava / lang / String;) V
      46 : 반환
}

당신이 볼 수 있듯이, 그것은 생성 StringBuilder및 사용을 append. 내장 버퍼의 기본 용량이 StringBuilder16 자에 불과하기 때문에 이것은 상당히 비효율적 인 것으로 유명 하며 컴파일러 가 미리 더 많은 할당을 알 수 있는 방법이 없으므로 결국 재 할당해야합니다. 또한 많은 메서드 호출입니다. (JVM은 때때로 이러한 호출 패턴을 감지하고 다시 작성하여 더 효율적으로 만들 수 있습니다.)

Java 9가 생성하는 것을 살펴 보겠습니다.

공용 클래스 예 {
  공개 예제 ();
    암호:
       0 : aload_0
       1 : invokespecial # 1 // 메소드 java / lang / Object. "<init>":() V
       4 : 반환

  public static void main (java.lang.String []);
    암호:
       0 : aload_0
       1 : 아이콘
       2 : aaload
       3 : aload_0
       4 : iconst_1
       5 : aaload
       6 : aload_0
       7 : iconst_2
       8 : aaload
       9 : invokedynamic # 2, 0 // InvokeDynamic # 0 : makeConcatWithConstants : (Ljava / lang / String; Ljava / lang / String; Ljava / lang / String;) Ljava / lang / String;
      14 : astore_1
      15 : getstatic # 3 // 필드 java / lang / System.out : Ljava / io / PrintStream;
      18 : aload_1
      19 : invokevirtual # 4 // 메소드 java / io / PrintStream.println : (Ljava / lang / String;) V
      22 : 반환
}

오,하지만 더 짧습니다. 🙂 makeConcatWithConstantsfrom에 대한 단일 호출을 수행 StringConcatFactory하며 Javadoc에 다음과 같이 표시됩니다.

유형 적응 및 인수의 부분 평가 후에 알려진 유형의 알려진 수의 인수를 효율적으로 연결하는 데 사용할 수있는 문자열 연결 메서드의 생성을 용이하게하는 메서드입니다. 이러한 메소드는 일반적으로 Java 프로그래밍 언어 의 문자열 연결 기능 을 지원 하기 위해 invokedynamic호출 사이트의 부트 스트랩 메소드 로 사용됩니다 .


답변

invokedynamic문자열 연결 최적화에 사용되는 구현 에 대한 세부 사항을 살펴보기 전에 invokedynamic이란 무엇이며 어떻게 사용합니까?


invokedynamic
명령어 는 JVM에서 동적 언어를위한 컴파일러 및 런타임 시스템의 구현을 단순화하고 잠재적으로 개선합니다 . 이는 언어 구현자가 invokedynamic다음 단계를 포함하는 명령어로 사용자 정의 연결 동작을 정의 할 수 있도록하여 수행합니다.


문자열 연결 최적화 구현을 위해 가져온 변경 사항을 통해 이러한 내용을 안내해 드릴 것입니다.

  • 부트 스트랩 방법을 정의 : – Java9에 대한 부트 스트랩 방법으로 invokedynamic호출 사이트 것은 주로 문자열 연결을 지원 makeConcat하고 makeConcatWithConstants도입했다 StringConcatFactory구현입니다.

    invokedynamic의 사용은 런타임까지 번역 전략을 선택하는 대안을 제공합니다. 에서 사용 된 번역 전략 은 이전 자바 버전에서 소개 된 StringConcatFactory것과 유사합니다 LambdaMetafactory. 또한 질문에 언급 된 JEP의 목표 중 하나는 이러한 전략을 더욱 확장하는 것입니다.

  • 상수 풀 항목 지정 :- invokedynamic명령어 MethodHandles.Lookup컨텍스트에서 메서드 핸들을 생성하는 팩토리 인 (1) 객체 invokedynamic, (2) String동적 호출에서 언급 된 메서드 이름 인 객체 이외 의 명령어에 대한 추가 정적 인수 입니다. 사이트 및 (3) MethodType개체, 동적 호출 사이트의 확인 된 형식 서명.

    코드를 연결하는 동안 이미 연결되어 있습니다. 런타임에 부트 스트랩 메서드는 연결을 수행하는 실제 코드에서 실행되고 연결됩니다. invokedynamic적절한 invokestatic호출로 호출을 다시 작성합니다 . 이렇게하면 상수 풀에서 상수 문자열이로드되고 부트 스트랩 메서드 정적 인수는 이러한 상수와 기타 상수를 부트 스트랩 메서드 호출에 직접 전달하는 데 활용됩니다.

  • invokedynamic 명령어 사용 :-초기 호출 중에 호출 대상을 한 번 부트 스트랩하는 수단을 제공하여 지연 연결 기능을 제공합니다. 여기서 최적화에 대한 구체적인 아이디어는 전체 StringBuilder.append댄스 를 연결이 필요한 값을 허용 하는를 invokedynamic호출 하는 간단한 호출로 대체 java.lang.invoke.StringConcatFactory하는 것입니다.

Indify 문자열 병합 예 제안서 공유 상태와 동일한 방법으로 Java9 애플리케이션의 벤치마킹 @TJ 크라우는 컴파일되고, 바이트 코드의 차이는 VARYING 구현 사이 상당히 보인다.


답변

여기에 약간의 세부 사항을 추가하겠습니다. 얻을 주요 부분은 문자열 연결이 수행되는 방식이 더 이상 컴파일 시간 이 아니라 런타임 결정이라는 것 입니다. 따라서 변경 될 수 있습니다. 즉, Java-9에 대해 코드를 한 번 컴파일했으며 재 컴파일 할 필요없이 원하는대로 기본 구현을 변경할 수 있습니다.

두 번째 요점은 현재 다음과 6 possible strategies for concatenation of String같습니다.

 private enum Strategy {
    /**
     * Bytecode generator, calling into {@link java.lang.StringBuilder}.
     */
    BC_SB,

    /**
     * Bytecode generator, calling into {@link java.lang.StringBuilder};
     * but trying to estimate the required storage.
     */
    BC_SB_SIZED,

    /**
     * Bytecode generator, calling into {@link java.lang.StringBuilder};
     * but computing the required storage exactly.
     */
    BC_SB_SIZED_EXACT,

    /**
     * MethodHandle-based generator, that in the end calls into {@link java.lang.StringBuilder}.
     * This strategy also tries to estimate the required storage.
     */
    MH_SB_SIZED,

    /**
     * MethodHandle-based generator, that in the end calls into {@link java.lang.StringBuilder}.
     * This strategy also estimate the required storage exactly.
     */
    MH_SB_SIZED_EXACT,

    /**
     * MethodHandle-based generator, that constructs its own byte[] array from
     * the arguments. It computes the required storage exactly.
     */
    MH_INLINE_SIZED_EXACT
}

매개 변수를 통해 이들 중 하나를 선택할 수 있습니다 -Djava.lang.invoke.stringConcat. 공지 StringBuilder여전히 옵션입니다.


답변