簡體   English   中英

Java字節碼操作'invokevirtual'不保持Object繼承的方法的一致性

[英]Java bytecode operation 'invokevirtual' does not keep consistency for the methods inherited by Object

我有以下代碼。

public class Parent {

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

}

public class Child extends Parent {

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

}

正如您在上面的代碼中看到的那樣,Child從Object繼承toString(),從Parent繼承hashCode()。 Child#test的字節碼操作如下。

ALOAD 0: this
INVOKEVIRTUAL Object.toString() : String
ALOAD 0: this
INVOKEVIRTUAL Child.hashCode() : int
RETURN

我認為如果invokevirtual調用Object.toString(),它應該調用Parent.hashCode()以保持一致性。 或者,調用Child.hashCode(),然后調用Child.toString()。

但是,當且僅當Object繼承目標方法時,invokevirtual才會保持其一致性。

只有這種情況,在Object中調用虛擬調用方法。 對於其他情況,請在當前類中調用虛擬調用方法。

我想知道為什么會這樣。

根據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;

你是正確的,編譯器的行為是非邏輯的。 但是此代碼的效果與您建議的兩種變體的效果相同。 所以這可能不是故意的行為,而是編譯器代碼長期演變的結果。 其他編譯器可能會生成不同的代碼。

我的理論: toString()經常使用,因此javac使用公共Object.toString()來保存常量池條目。

例如,如果代碼包含foo.toString() and bar.toString() ,則只需要一個Object.toString ,而不是兩個條目Foo.toString and Bar.toString

Javac可能硬編碼了這種優化,而不是分析代碼以確定它是否真的需要。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM