StackOverFlow 2021년 4월 Best Question
Tags: English To Korean, JVM, Java, Stackoverflow, Translation

스택오버플로우 많은 Votes를 받은 질문 과 그 답변을 해석합니다.

저는 영어공부를 시작한지 얼마 안된 초보입니다. 이 점 유의해 주세요.

의역 및 오역 지적 환영합니다.

Why does the java compiler 11 use invokevirtual to call private method?

Java Compiler 11이 invokevirtual을 사용하여 private Method를 호출하는 이유가 무엇입니까?

OpenJDK 8에서 아래의 코드를 자바 컴파일을 할 때, foo()를 invokespecial을 통해 호출한다. 하지만 OpenJDK11에서는 invokevirtual을 사용한다.

public class Invoke {

public void call() {

foo();

} private void foo() {}

}

JAVA 1.8.0_282 버전을 사용한 출력결과

public void call();

descriptor: ()V

flags: (0x0001) ACC_PUBLIC

Code:

stack=1, locals=1, args_size=1

0: aload_0

1: invokespecial #2 // Method foo:()V

4: return

JAVA 11.0.10 버전을 사용한 출력 결과

public void call();

descriptor: ()V

flags: (0x0001) ACC_PUBLIC

Code:

stack=1, locals=1, args_size=1

0: aload_0

1: invokevirtual #2 // Method foo:()V

4: return

foo()를 오버라이드 하면서 invokevirtual 를 왜 사용하는지 모르겠다.

이를 알아보자면 invokevirtual 의 목적은 중첩 class에서 outer class 안에 있는 private method를 호출 할 수 있도록 하는 것으로 보인다.

그래서 아래 코드를 실행시켜 보았다.

public class Test{

public static void main(String[] args) {

// Build a Derived such that Derived.getValue()

// somewhat "exists".

System.out.println(new Derived().foo());

} public static class Base { public int foo() {

return getValue() + new Nested().getValueInNested();

} private int getValue() {

return 24;

} private class Nested { public int getValueInNested() {

// This is getValue() from Base, but would

// invokevirtual call the version from Derived?

return getValue();

}

}

} public static class Derived extends Base { // Let's redefine getValue() to see if it is picked by the

// invokevirtual from getValueInNested().

private int getValue() {

return 100;

}

}

}

JDK 11을 사용해 컴파일한 결과, invokevirtual 이 foo() 와 getValueInNested() 에서 모두 사용중인 것을 javap 를 사용해서 볼 수 있다.

descriptor: ()I

flags: (0x0001) ACC_PUBLIC

Code:

stack=4, locals=1, args_size=1

0: aload_0 // ** HERE **

1: invokevirtual #2 // Method getValue:()I

4: new #3 // class Test$Base$Nested

7: dup

8: aload_0

9: invokespecial #4 // Method Test$Base$Nested."<init>":(LTest$Base;)V

12: invokevirtual #5 // Method Test$Base$Nested.getValueInNested:()I

15: iadd

16: ireturn public int getValueInNested();

descriptor: ()I

flags: (0x0001) ACC_PUBLIC

Code:

stack=1, locals=1, args_size=1

0: aload_0

1: getfield #1 // Field this$0:LTest$Base; // ** HERE **

4: invokevirtual #3 // Method Test$Base.getValue:()I

7: ireturn

이는 약간 혼란스럽고 몇 가지 의문을 남긴다.

Why invokevirtual is used to call private methods? Is there a use case where replacing it by an invokespecial would not be equivalent?

왜 invokevirtual 을 사용하여 private method를 호출하는가?

How does the call to getValue() in Nested.getValueInNested() not pick the method from Derived since it is called via invokevirtual ?

2. Nested.getValueInNested() 의 getValue() 호출은 invokevirtual 을 통해 호출되기 때문에 파생 클래스(자식 클래스)에서 메소드를 호출하지 않는 방법은 무엇입니까?

질문에 대한 답변

This was done as part of https://openjdk.java.net/jeps/181: Nest-Based Access Control, so that the JVM can allow access to private methods from nested classes.

https://openjdk.java.net/jeps/181 에서 Nest-Based Access Control 부분을 사용하여 수행했습니다, JVM 은 중첩 class에서 private method들을 접근할 수 있습니다.

Before that change, the compiler would have to generate a package-protected synthetic method in the Base class, which the nested class invokes. That synthetic method would in turn call the private method in the Base class. The feature in Java 11 enhances the JVM to allow that without the compiler having to generate a synthetic method.

바뀌기 이전에, 컴파일러는 중첩 class가 호출하는 package-protected 에서 Base class(기초 클래스) 에서 synthetic method 을 생성해야 한다. synthetic method는 Base class의 private method를 차례로 호출합니다. JAVA11에서는 직접 synthetic method 을 생성하지 않고 허용하여 JVM 성능을 향상 시킵니다.

Concerning the point on whether invokevirtual will call the method in the Derived class, the answer is no The private method is still not subject to method selection of the runtime class (this has never changed)

invokevirtual 이 Derived 클래스를 호출할 수 있는가에 대한 여부는 ‘그렇지 않다’ 이다. private method는 Method Selection에 포함되지 않는다. (이는 절대 변하지 않을 것이다..)

MethodSelection에 관한 내용

During execution of an invokeinterface or invokevirtual instruction, a method is selected with respect to (i) the run-time type of the object on the stack, and (ii) a method that was previously resolved by the instruction. The rules to select a method with respect to a class or interface C and a method mR are as follows:

invokevirtual 또는 invokeinterface 이 실행되는 동안 method는 스택에 있는 object의 런타임 유형에 선택된다. 그리고 이전에 있었던 method는 instruction에 의해 resolved가 된다. method에 관하여 선택할때의 규칙은 a class 또는 c interface여야 한다.

자세한 내용 : https://docs.oracle.com/javase/specs/jvms/se11/html/jvms-5.html#jvms-5.4.6

Based on the comment “Would it be valid to still use invokespecial if the private method is called from the method owner class and use invokevirtual if it is called from a nested class?”

이 코멘트는 이 질문에 기반을 두었다. “valid 을 invokespcial 을 사용했을 때 private method가 owner class를 호출하게 되는 경우 그리고 invokespcial 사용했을때 만약 nasted class를 호출한다면?