JEP 280에 쓰여진대로 : Indify String Concatenation :
JDK 라이브러리 함수에 대한 호출을 사용하기 위해에서
String
생성 된 정적 연결 바이트 코드 시퀀스를 변경합니다 . 이렇게하면에 의해 방출 된 바이트 코드를 추가로 변경하지 않고도 향후 연결 최적화가 가능해집니다 .javac
invokedynamic
String
javac
여기에서 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
. 내장 버퍼의 기본 용량이 StringBuilder
16 자에 불과하기 때문에 이것은 상당히 비효율적 인 것으로 유명 하며 컴파일러 가 미리 더 많은 할당을 알 수 있는 방법이 없으므로 결국 재 할당해야합니다. 또한 많은 메서드 호출입니다. (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 : 반환 }
오,하지만 더 짧습니다. 🙂 makeConcatWithConstants
from에 대한 단일 호출을 수행 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
여전히 옵션입니다.