Java字节码操作'invokevirtual'不保持Object继承的方法的一致性

Hoz*_*ard 5 java jvm bytecode invokevirtual

我有以下代码.

public class Parent {

    @Override
    public int hashCode() {
         return 0;
    }

}

public class Child extends Parent {

    public void test() {
        this.toString();
        this.hashCode();
    }

}
Run Code Online (Sandbox Code Playgroud)

正如您在上面的代码中看到的那样,Child从Object继承toString(),从Parent继承hashCode().Child#test的字节码操作如下.

ALOAD 0: this
INVOKEVIRTUAL Object.toString() : String
ALOAD 0: this
INVOKEVIRTUAL Child.hashCode() : int
RETURN
Run Code Online (Sandbox Code Playgroud)

我认为如果invokevirtual调用Object.toString(),它应该调用Parent.hashCode()以保持一致性.或者,调用Child.hashCode(),然后调用Child.toString().

但是,当且仅当Object继承目标方法时,invokevirtual才会保持其一致性.

只有这种情况,在Object中调用虚拟调用方法.对于其他情况,请在当前类中调用虚拟调用方法.

我想知道为什么会这样.

And*_*niy 5

根据JVM规范p.3.7:

编译器不知道类实例的内部布局.相反,它生成对实例方法的符号引用,这些引用存储在运行时常量池中.在运行时解析这些运行时常量池项以确定实际的方法位置.

这意味着所有这些符号Child.hashCode()都只是常量,而没有指定JVM如何调用此方法.看来,toString()方法编译器知道,这个方法在Object类中有它的基本实现,所以它把符号常量Object放到常量池中的类 - 这是某种优化,这使得JVM的编译器:

  Constant pool:
const #2 = Method   #24.#25;    //  java/lang/Object.toString:()Ljava/lang/String;
...
const #24 = class   #34;    //  java/lang/Object
const #25 = NameAndType #35:#36;//  toString:()Ljava/lang/String;
Run Code Online (Sandbox Code Playgroud)


Ale*_*dov 4

你是对的,编译器的行为不合逻辑。但此代码的效果与您建议的两种变体的效果相同。因此,这可能不是有意的行为,而是编译器代码长期演变的结果。其他编译器可能会生成不同的代码。