2017-03-05 5 views
1

나는 다음과 같은 자바 프로그램class.NewInstance()를 호출 할 때 InvokeSpecial 대신 InvokeVirtual이 사용되는 이유는 무엇입니까?

public class ASMPlayground { 
    private String bar; 

    public String getBar(){ 
     return bar; 
    } 

    public void setBar(String bar) throws IllegalAccessException, InstantiationException { 
     String name = String.class.newInstance(); 
     System.out.println(name); 
    } 

    public static void main(String[] args) { 

    } 
} 

다음 바이트 코드 조각이 내 눈을 사로 잡은 및 차선 보였다의 분해를 조사했다

LDC Ljava/lang/String;.class 
INVOKEVIRTUAL java/lang/Class.newInstance()Ljava/lang/Object; 
CHECKCAST java/lang/String 

질문 :

InvokeVirtual 사용 실행될 메소드가 객체 참조에 의존 할 때. "Class"는 final이며 newInstance()는 "Class"에 만 존재하므로 InvokeVirtual 대신 InvokeSpecial을 사용하지 않는 이유는 무엇입니까? 더 많은 공연을하지 않겠습니까?

답변

3

InvokeSpecial

사양

수퍼 전용 및 인스턴스 초기화 메소드 호출되어 호출을 지정하는 데 사용된다 https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-6.html#jvms-6.5.invokespecial

Class.newInstance는 수퍼 메소드 호출이나 프라이빗 메소드 호출하지도 초기화 메소드를 호출하지 마십시오. 또한 동적 방법이 아니기 때문에 InvokeDynamic은 여기에 할 일이 없습니다. 두 명령어 모두 jvms와 충돌하기 때문에 여기서는 사용할 수 없습니다. jvms를 만들 때 "더 성능이 좋은"명령어로 대체 할 수 있지만 jvms에서 볼 수 있듯이 완료되지 않았습니다.

성능이 더 좋지 않습니까?

JIT는 이러한 경우 가상 메소드 테이블을 탐색 할 필요가 없다는 것을 충분히 이해하므로 실행이 느려져서는 안됩니다. 실제 성능은 실제 테스트로 비교해야하지만 상당한 차이가있을 이유는 없습니다.

+1

특히'Class'는'final'이므로 유일한 가상 메소드는 부모 클래스 메소드 호출입니다. –

+0

나는 편집을했다. InvokedDynamic은 오타였습니다. 나는 InvokeVirtual을 말하려고했다. – KodeWarrior

+0

InvokeSpecial의 설명서를 확인했습니다. 또한 InvokeVirtual을 확인했습니다. https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-6.html#jvms-6.5.invokevirtual 인스턴스 메소드를 호출합니다. 클래스를 기반으로 디스패치 주어진 클래스가 여기에 invokevirtual이 사용 된 이유를 여전히 이해할 수 없습니다. InvokedSpecial opcode가 같은/super 클래스의 메서드에서만 작동 할 수 있다고 대답 할 수 있습니다. – KodeWarrior

0

대상 메서드 또는 클래스가 final이라는 사실은 컴파일 된 폼을 다른 클래스를 사용하여 변경하거나 메서드를 호출하지 않습니다.

이는 JLS, 제 13 장 "이진 호환성", §13.4.17, final methods의 명령에 따른 것입니다 :

final가 더 이상 기존의 바이너리와의 호환성은 영향을받지 않습니다 final 선언되지 선언하는 방법을 변경. final가 더 이상 기존의 바이너리와의 호환성은 영향을받지 않습니다 final 선언되지 선언하는 클래스를 변경 마찬가지로 §13.4.2. final Classes

.

분명히 대상 메서드의 final 자연에 의존하는 호출 명령은이 사양을 위반하게됩니다.

예상되는 성능 영향은 없습니다.심볼릭 참조를 실제 메소드 (JVM의 런타임 표현의 관점에서)로 해결해야하는 경우 항상 오버 헤드가 발생합니다. 이 시점에서 JVM은 이점이있는 경우이 메소드 또는 해당 선언 클래스가 호출 명령어에 실제로 final이라는 사실을 기록 할 수도 있습니다. 현대 JVM은 더 멀리 나아 간다. 비 final 메소드가 실제로 동일한 효과로 겹쳐 쓰지는 않는다는 사실을 이용하십시오. 서브 클래스가로드되고 인스턴스화되면 오버라이드 (override)하는 메소드가있는 경우 이러한 호출을 최적화 해제해야합니다. 따라서 유일한 차이점은 final 수정자가 이러한 최적화가 필요하지 않음을 보장한다는 것입니다.

+0

'final'은 실제로 아무것도 보장합니까? 수정자를 런타임에 제거 할 수 있으며 컴파일 중에 가짜 소스를 대체 할 수 있습니다. 클래스 로딩은 게으르므로 허용되어야한다고 생각했습니다. 그렇지 않으면 링크가 있습니까? –

+0

@Sergey Fedorov : 글쎄, 내 대답의 첫 부분에서 알 수 있듯이 클래스가로드되기 전에 * Binary Compatibility * 사양에서 요구하는대로 'final'은 전혀 영향을 미치지 않습니다. 내 대답의 마지막 문장은 클래스가로드 된 후에 만 ​​JVM과 관련이 있으며 JVM은 메서드를 재정의하지 않는다는 가정하에 최적화를 시작했습니다. 메소드가 final 인 경우 JVM은 대체 시도를 거부합니다. 그러나 이는 여전히 JVM 구현에만 해당됩니다. 예 : JVM이 Instrumentation을 통해'final'을 제거 할 수 있다면, 그 경우에는 최적화 해제를 지원해야합니다. – Holger

+0

afaik, hotspot jit는 최적화 해제를 지원하지만 이는 중요하지 않습니다. 한정자는 jvm에만 국한된 것이 아니라 단지 필드와 함께 작동하는 리플렉션으로 제거 할 수 있습니다. 지금까지는 메소드와 함께 작동해야한다고 생각했기 때문에 실제로 'final'은 컴파일이 끝난 후에 아무 것도 의미하지 않습니다. 이 사실을 증명할 수있는 증거가 있습니까? –