답변:
이것은 새로운 JVM 명령어로, 컴파일러가 이전보다 더 느슨한 사양으로 메소드를 호출하는 코드를 생성 할 수있게합니다. " 덕 타이핑 "이 무엇인지 알고 있다면 invokedynamic은 기본적으로 오리 타이핑을 허용합니다. 자바 프로그래머가 할 수있는 것만 큼 많지는 않다. 툴 제작자라면이를 사용하여보다 유연하고 효율적인 JVM 기반 언어를 구축 할 수 있습니다. 여기 에 많은 세부 사항을 제공하는 정말 달콤한 블로그 게시물이 있습니다.
MethodHandle은 실제로 같은 종류이지만 훨씬 더 유연합니다. 그러나이 모든 것의 진정한 힘은 Java 언어에 추가되는 것이 아니라 본질적으로 더 역동적 인 다른 언어를 지원하는 JVM 자체의 기능에 있습니다.
invokedynamic성능을 향상시키는 것으로 보입니다 (소개하기 전에 거의 유일한 선택 인 익명 내부 클래스로 래핑하는 것과 비교 invokedynamic). JVM 위에 많은 기능적 프로그래밍 언어가 아마도 내부 클래스 대신에 이것을 컴파일하도록 선택할 것입니다.
얼마 전에 C #은 C # 내에 멋진 기능, 동적 구문을 추가했습니다.
Object obj = ...; // no static type available
dynamic duck = obj;
duck.quack(); // or any method. no compiler checking.
반사 메소드 호출의 구문 설탕으로 생각하십시오. 매우 흥미로운 응용 프로그램을 가질 수 있습니다. 참조 http://www.infoq.com/presentations/Statically-Dynamic-Typing-Neal-Gafter를
C #의 동적 유형을 담당하는 Neal Gafter는 SUN에서 MS로 결함이 있습니다. 따라서 같은 일이 SUN 내부에서 논의되었다고 생각하는 것은 무리가 없습니다.
얼마 지나지 않아 일부 Java 친구가 비슷한 것을 발표했습니다.
InvokeDynamic duck = obj;
duck.quack();
불행히도이 기능은 Java 7에서 찾을 수있는 곳이 아닙니다. 매우 실망했습니다. Java 프로그래머에게는 프로그램에서 쉽게 활용할 수있는 방법이 없습니다 invokedynamic.
invokedynamic않았다 의도가 자바 프로그래머에 사용할 수 있습니다. IMO는 Java 철학에 전혀 맞지 않습니다. Java 이외의 언어에 대한 JVM 기능으로 추가되었습니다.
invokedynamic을 계속하기 전에 이해해야 할 두 가지 개념이 있습니다.
1. 정적 대 다이너 민 입력
정적 -컴파일 타임에 형식 검사를 수행합니다 (예 : Java)
동적 -런타임시 형식 검사를 수행합니다 (예 : JavaScript)
형식 검사는 프로그램이 형식에 안전한지 확인하는 프로세스입니다. 즉, 클래스 및 인스턴스 변수, 메서드 매개 변수, 반환 값 및 기타 변수에 대한 형식화 된 정보를 검사합니다. 예를 들어 Java는 컴파일 타임에 int, String, ..에 대해 알고 있지만 JavaScript의 객체 유형은 런타임에만 결정할 수 있습니다.
2. 강한 대 약한 타이핑
강력 함 -조작에 제공된 값 유형에 대한 제한 사항을 지정합니다 (예 : Java).
약함 -조작의 인수가 호환되지 않는 유형 (예 : Visual Basic) 인 경우 조작의 인수를 변환 (캐스트)합니다.
Java가 정적이고 약한 유형임을 알고 JVM에서 동적 및 강력 유형 언어를 어떻게 구현합니까?
invokedynamic은 프로그램이 컴파일 된 후 가장 적합한 메소드 또는 함수 구현을 선택할 수있는 런타임 시스템을 구현합니다.
예 : (a + b)를 가지고 컴파일 타임에 변수 a, b에 대해 아는 것은 invokedynamic이 런타임에 Java에서 가장 적합한 메소드에이 조작을 맵핑합니다. 예를 들어, a, b가 문자열 인 경우 method (String a, String b)를 호출하십시오. a, b가 int 인 경우 method (int a, int b)를 호출하십시오.
invokedynamic은 Java 7에서 도입되었습니다.
Java Records 기사의 일부로 Inoke Dynamic의 동기에 대해 설명했습니다. Indy의 대략적인 정의부터 시작하겠습니다.
Invoke Dynamic ( Indy 라고도 함 )은 동적 유형 언어에 대한 JVM 지원을 향상시키기 위해 JSR 292의 일부였습니다 . Java 7에서 첫 번째 릴리스 이후, 수하물 invokedynamic과 함께 opcode java.lang.invoke는 JRuby와 같은 동적 JVM 기반 언어에서 상당히 광범위하게 사용됩니다.
인디는 동적 언어 지원을 향상시키기 위해 특별히 설계되었지만 그 이상을 제공합니다. 사실, 언어 디자이너가 다이나믹 타입 곡예부터 다이나믹 전략에 이르기까지 모든 형태의 다이나믹이 필요한 곳에 사용하는 것이 적합합니다!
예를 들어, Java가 정적으로 유형이 지정되었지만 Java 8 Lambda Expressions는 실제로를 사용하여 구현 invokedynamic됩니다!
꽤 오랫동안 JVM은 invokestatic정적 메소드 invokeinterface호출, 인터페이스 메소드 invokespecial호출, 생성자 super()또는 개인 메소드 invokevirtual호출 및 인스턴스 메소드 호출의 네 가지 메소드 호출 유형을 지원했습니다 .
이들의 차이점에도 불구하고, 이러한 호출 유형은 하나의 공통된 특성을 공유 합니다 . 자체 논리로이를 강화할 수는 없습니다 . 반대로, invokedynamic 원하는 방식으로 호출 프로세스를 부트 스트랩 할 수 있습니다. 그런 다음 JVM은 Bootstrapped Methods를 직접 호출합니다.
JVM이 invokedynamic명령어를 처음 볼 때 Bootstrap Method 라는 특수 정적 메소드를 호출 합니다 . 부트 스트랩 메소드는 호출 할 실제 로직을 준비하기 위해 작성한 Java 코드입니다.
그런 다음 부트 스트랩 메소드는의 인스턴스를 리턴합니다 java.lang.invoke.CallSite. 이것은 CallSite실제 방법에 대한 참조를 보유합니다 MethodHandle.
이제부터 JVM이이 invokedynamic명령을 다시 볼 때마다 느린 경로를 건너 뛰고 기본 실행 파일을 직접 호출합니다. 변경 사항이 없으면 JVM은 느린 경로를 계속 건너 뜁니다.
Java 14 Records는 벙어리 데이터 홀더로 간주되는 클래스를 선언하는 멋진 간단한 구문을 제공합니다.
이 간단한 기록을 고려하면 :
public record Range(int min, int max) {}
이 예제의 바이트 코드는 다음과 같습니다.
Compiled from "Range.java"
public java.lang.String toString();
descriptor: ()Ljava/lang/String;
flags: (0x0001) ACC_PUBLIC
Code:
stack=1, locals=1, args_size=1
0: aload_0
1: invokedynamic #18, 0 // InvokeDynamic #0:toString:(LRange;)Ljava/lang/String;
6: areturn
그것에서 부트 스트랩 방법 표 :
BootstrapMethods:
0: #41 REF_invokeStatic java/lang/runtime/ObjectMethods.bootstrap:
(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;
Ljava/lang/invoke/TypeDescriptor;Ljava/lang/Class;
Ljava/lang/String;[Ljava/lang/invoke/MethodHandle;)Ljava/lang/Object;
Method arguments:
#8 Range
#48 min;max
#50 REF_getField Range.min:I
#51 REF_getField Range.max:I
따라서 클래스에 상주하는 레코드 의 부트 스트랩 메소드가 호출 됩니다. 보다시피,이 부트 스트랩 방법에는 다음과 같은 매개 변수가 필요합니다.bootstrapjava.lang.runtime.ObjectMethods
MethodHandles.Lookup조회 컨텍스트 를 나타내는 인스턴스 ( Ljava/lang/invoke/MethodHandles$Lookup부분).toString, equals, hashCode, 등) 부트 스트랩은 링크 것입니다. 예를 들어, 값이 인 toString경우 부트 스트랩은 이 특정 레코드 의 실제 구현을 가리키는 ConstantCallSite(a CallSite는 절대 변경되지 않음)을 반환합니다 toString.TypeDescriptor방법 (용 Ljava/lang/invoke/TypeDescriptor
부분).Class<?>Record 클래스 유형을 나타냅니다. 그것은이다
Class<Range>이 경우.min;max.MethodHandle구성 요소 당 하나 . 이 방법으로 부트 스트랩 메소드는 MethodHandle이 특정 메소드 구현을위한 컴포넌트를 기반으로 작성할 수 있습니다 .invokedynamic명령은 부트 스트랩 방법에 대한 모든 인수를 전달합니다. 부트 스트랩 메소드는의 인스턴스를 반환합니다 ConstantCallSite. 이것은 ConstantCallSite예를 들어, 요구 된 방법의 구현에 대한 참조를 보유한다 toString.
Reflection API와 달리 java.lang.invokeJVM은 모든 호출을 완전히 볼 수 있기 때문에 API가 매우 효율적입니다. 따라서 가능한 한 느린 경로를 피하는 한 JVM은 모든 종류의 최적화를 적용 할 수 있습니다!
효율성 주장 외에도 invokedynamic접근 방식은 단순성으로 인해보다 안정적이고 덜 취약 합니다.
또한 Java 레코드에 대해 생성 된 바이트 코드는 특성 수와 무관합니다. 따라서 적은 바이트 코드와 빠른 시작 시간.
마지막으로, 새 버전의 Java에 새롭고보다 효율적인 부트 스트랩 메소드 구현이 포함되어 있다고 가정합니다. 을 사용하면 invokedynamic앱이 재 컴파일없이이 개선 사항을 활용할 수 있습니다. 이런 식으로 우리는 일종의 정방향 바이너리 호환성을 갖습니다 . 또한 이것이 우리가 이야기 한 역동적 인 전략입니다!
Java Records 외에도 invoke dynamic 은 다음과 같은 기능을 구현하는 데 사용되었습니다.
LambdaMetafactoryStringConcatFactory
meth.invoke(args). 그렇다면 어떻게invokedynamic맞meth.invoke습니까?