invokedynamic이란 무엇이며 어떻게 사용합니까?


159

JVM에 추가되는 모든 새로운 멋진 기능에 대해 계속 듣고 있으며 이러한 멋진 기능 중 하나는 invokedynamic입니다. 나는 그것이 무엇인지 알고 싶습니다. 어떻게 Java에서 반사 프로그래밍을 더 쉽게 또는 더 좋게합니까?

답변:


165

이것은 새로운 JVM 명령어로, 컴파일러가 이전보다 더 느슨한 사양으로 메소드를 호출하는 코드를 생성 할 수있게합니다. " 덕 타이핑 "이 무엇인지 알고 있다면 invokedynamic은 기본적으로 오리 타이핑을 허용합니다. 자바 프로그래머가 할 수있는 것만 큼 많지는 않다. 툴 제작자라면이를 사용하여보다 유연하고 효율적인 JVM 기반 언어를 구축 할 수 있습니다. 여기 에 많은 세부 사항을 제공하는 정말 달콤한 블로그 게시물이 있습니다.


3
일상적인 Java 프로그래밍에서 리플렉션이로 메소드를 동적으로 호출하는 데 사용되는 것은 드문 일이 아닙니다 meth.invoke(args). 그렇다면 어떻게 invokedynamicmeth.invoke습니까?
David K.

1
내가 언급 한 블로그 게시물 MethodHandle은 실제로 같은 종류이지만 훨씬 더 유연합니다. 그러나이 모든 것의 진정한 힘은 Java 언어에 추가되는 것이 아니라 본질적으로 더 역동적 인 다른 언어를 지원하는 JVM 자체의 기능에 있습니다.
어니스트 프리드먼


1
Java 8은 람다 중 일부를 사용하여 invokedynamic성능을 향상시키는 것으로 보입니다 (소개하기 전에 거의 유일한 선택 인 익명 내부 클래스로 래핑하는 것과 비교 invokedynamic). JVM 위에 많은 기능적 프로그래밍 언어가 아마도 내부 클래스 대신에 이것을 컴파일하도록 선택할 것입니다.
Nader Ghanbari

2
2008 년의 블로그 게시물은 절망적으로 구식이며 실제 릴리스 상태 (2011)를 반영하지 않습니다.
Holger

9

얼마 전에 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.


41
invokedynamic않았다 의도가 자바 프로그래머에 사용할 수 있습니다. IMO는 Java 철학에 전혀 맞지 않습니다. Java 이외의 언어에 대한 JVM 기능으로 추가되었습니다.
마크 피터스

5
@Mark 누구도 의도하지 않았습니까? 자바 유명 인사들에게 명확한 권력 구조가 있거나 잘 정의 된 집단 "의도"가있는 것과는 다릅니다. 언어 철학에 관해서는 - 그것은 아주으로 가능, 닐 Gafter (배신자!)를 참조 설명 : infoq.com/presentations/Statically-Dynamic-Typing-Neal-Gafter
irreputable

3
@mark peters : invokedynamic은 실제로 직접 접근 할 수없는 자바 프로그래머를위한 것입니다. Java 8 폐쇄의 기초입니다.
M Platvoet

2
@irreputable : JSR 기고자가 의도 한 것은 아닙니다. JSR의 이름은 "Java 플랫폼에서 동적 유형 언어 지원"이라고합니다. Java는 동적으로 유형이 지정된 언어가 아닙니다.
Mark Peters

5
@M Platvoet : 클로저를 최신 상태로 유지하지는 않았지만 클로저에 절대적인 요구 사항은 아닙니다. 그들이 논의한 또 다른 옵션은 VM 사양 변경없이 수행 할 수있는 익명의 내부 클래스에 대한 구문 구문을 간단하게 만드는 것입니다. 그러나 제 요점은 JSR이 동적 타이핑을 Java 언어로 가져 오려는 의도가 아니라는 것입니다.
마크 피터스

4

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에서 도입되었습니다.


4

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 레코드

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 은 다음과 같은 기능을 구현하는 데 사용되었습니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.