2014-06-11 4 views
0

TL

잘 알려진 동적 언어 (예 : JavaScript)로 작성된 코드와 invokedynamic을 사용하는 Java 바이트 코드에서 코드가 어떻게 나타나는지, invokedynamic의 사용법이 앞으로 나아갈 이유를 설명하십시오 . 내가 봤와 인터넷에 모두가 JVM에 속도 동적 언어를 도움이 될 것이라고에 동의 한 것으로 간주되는없는, 즉 새로운-더 이상 invokedynamic 명령에 대해 꽤 많이 읽고 언제 invokedynamic이 (게으른 상수 외에) 유용합니까?

배경입니다. Thanks to stackoverflow Sable/Jasmin으로 바이트 코드 명령어를 직접 실행할 수있었습니다.

나는 invokedynamic가 게으른 상수에 유용하다는 것을 이해했으며, 나는 또한 어떻게 이해했는지 생각한다. the OpenJDK takes advantage of invokedynamic for lambdas.

오라클은 a small example이 있지만, 지금까지의 내가이 경우 invokedynamic의 사용을 말할 수있는 "가산"에 대한 예제로 목적을 격파 할 수 훨씬 더 간단, 신속하고 다음 바이트 코드로 표현 거의 같은 효과 :

aload whereeverAIs 
checkcast java/lang/Integer 
aload whereeverBIs 
checkcast java/lang/Integer 
invokestatic IntegerOps/adder(Ljava/lang/Integer;Ljava/lang/Integer;)Ljava/lang/Integer; 

어떤 이유로 오라클의 부트 스트랩 방법은 두 인수가 정수 어쨌든 것을 알고 있기 때문에. 그들은 심지어 그것을 "인정"한다 :

[..] [..] 인수는 [Integer] 객체라고 가정한다. 부트 스트랩 방법의 매개 변수 (이 예제에서는 callerClass, dynMethodName 및 dynMethodType)가 다른 경우 부트 스트랩 방법을 사용하려면 invokedynamic [..]을 올바르게 연결하는 추가 코드가 필요합니다.

물론 그렇습니다. 여기에 invokedynamic을 사용하는 데 "추가 코드"가 필요하지 않습니다.

그렇기 때문에 Javadoc과 Blog의 두 항목을 추가하면 invokestatic/invokevirtual/invokevirtual 또는 getfield가 제대로 작동 할 때 invokedynamic을 사용하지 않는 방법을 잘 알고 있다고 생각합니다.

이제 우리는 실제적인 유즈 케이스에 실제로 invokedynamic 명령을 적용하여 "전통적인"호출 (게으른 상수를 제외하고 나는 ...)을 통해 할 수있는 것보다 개선 된 점을 알고 싶습니다.

답변

3

"지연 생성"이라는 용어를 널리 사용하는 경우 실제로는 게으른 작업이 invokedynamic의 주요 이점입니다. 예를 들어, Java 8의 람다 작성 기능은 invokedynamic 명령어에 의해 최종적으로 호출 될 코드가 포함 된 실제 클래스가 해당 명령어를 실행하기 전에 존재하지 않을 수도있는 가능성을 포함하는 지연 생성의 일종입니다.

이것은 자바 바이트 코드와 다른 형태로 코드를 제공하는 모든 종류의 스크립팅 언어에 투영 될 수 있습니다 (소스 코드에서도 가능할 수 있음). 여기서 코드는 메소드의 첫 번째 호출 바로 전에 컴파일 될 수 있으며 나중에 링크 된 상태로 유지됩니다. 그러나 스크립팅 언어가 메서드의 재정의를 지원하면 연결이 끊어 질 수도 있습니다. 이것은 invokedynamic이라는 두 번째 중요한 기능을 사용하여 변경할 수있는 CallSite을 허용하며, 나중에 재정의하지 않고 최대 성능을 지원하면서 변경 될 수 있습니다.

나중에 invokedynamic 타겟을 변경하면 첫 번째 호출에서 해석 된 실행에 연결하고 실행 횟수를 계산하고 임계 값을 초과 한 후에 만 ​​코드를 컴파일 한 다음 컴파일 된 코드와 다시 연결하는 다른 옵션을 사용할 수 있습니다. 런타임 인스턴스에 기초하여 동적 인 방식의 디스패치에 관해서


그것은 invokedynamic는 디스패치 알고리즘을 생략하다 수 없다는 것을 분명하다. 그러나 특정 호출 사이트가 항상 동일한 콘크리트 유형의 메소드를 호출 할 것이라는 것을 런타임에 감지하면 CallSite을 최적화 된 코드로 다시 연결할 수 있습니다. 그러면 대상이 예상 유형이고 최적화 된 조치를 수행하는 경우 짧은 점검을 수행합니다. 해당 테스트가 실패 할 경우에만 전체 동적 디스패치를 ​​수행하는 일반 코드로 분기합니다. 구현은 고속 경로 검사가 일정 횟수 실패한 것을 감지하면 이러한 호출 사이트를 최적화 해제 할 수 있습니다.

invokevirtualinvokeinterface은 JVM에서 내부적으로 최적화 된 방법과 비슷합니다. 이러한 지침의 대부분은 동일한 구체적인 유형으로 호출되는 경우도 마찬가지입니다. 따라서 invokedynamic을 사용하면 임의의 조회 알고리즘에 대해 동일한 기법을 사용할 수 있습니다.


하지만 당신은 완전히 다른 사용 사례를 원한다면, 당신은 표준 액세스 수정 규칙에 의해 지원되지 않습니다 friend 의미를 구현하는 invokedynamic를 사용할 수 있습니다. 클래스 AB이 있으며 AB의 메소드를 호출 할 수 있도록 friend 관계를 갖는 클래스라고 가정합니다.

public static CallSite bootStrap(Lookup l, String name, MethodType type) 
    throws NoSuchMethodException, IllegalAccessException { 
    if(l.lookupClass()!=A.class || (l.lookupModes()&0xf)!=0xf) 
     throw new SecurityException("unprivileged caller"); 
    l=MethodHandles.lookup(); 
    return new ConstantCallSite(l.findStatic(B.class, name, type)); 
} 

을 처음 제공된 Lookup 개체에 대한 전체 액세스 권한이 있는지 확인 : 그럼이 모든 호출은 다음과 같이 보일 수 있습니다 원하는 이름과 B에서 public 부트 스트랩 방법에 서명 가리키는 invokedynamic 지시로 인코딩 할 수있다 AA만이 그러한 대상을 구성 할 수 있습니다. 그래서 잘못된 발신자의 교활한 시도가이 장소에서 분류됩니다. 그런 다음 B에 대한 모든 액세스 권한을 가진 Lookup 개체를 사용하여 연결을 완료합니다. 따라서 이러한 invokedynamic 명령어는 각각 private 메서드와 첫 번째 호출 후 B 메서드에 영구적으로 연결되며 이후에는 일반 호출과 동일한 속도로 실행됩니다.