![](/img/trans.png)
[英]Java bytecode operation 'invokevirtual' does not keep consistency for the methods inherited by Object
[英]Java Bytecode: invokevirtual methodref on an objectref with a different class
我目前正在研究 Java 字節碼的工作原理。 我創建了這個簡單的測試類:
class Main {
public static void main(String[] args) {
System.out.println("Hello, World!");
}
}
使用javap -c Main.class
我可以獲得它的字節碼:
class Main {
Main();
Code:
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: return
public static void main(java.lang.String[]);
Code:
0: getstatic #7 // Field java/lang/System.out:Ljava/io/PrintStream;
3: ldc #13 // String Hello, World!
5: invokevirtual #15 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
8: return
}
應該執行的第一條指令(根據我的理解)應該是main
函數中的getstatic
指令。 這會導致加載System
類和其他類似Console
的類。
在Console
的<clinit>
方法期間:
static {};
Code:
0: ldc #2 // class java/io/Console
2: invokevirtual #203 // Method java/lang/Class.desiredAssertionStatus:()Z
...
執行invokevirtual
方法以調用類Class
上的desiredAssertionStatus
函數。 已經可以看出此invokevirtual
指令與上述指令之間的區別: javap
將類名附加在方法名之前,因為該函數是在類Class
中找到的,而不是在Console
中。
所以最后我的問題是:這里發生了什么? 根據JVM 19 的invokevirtual
規范,該方法應該在堆棧上的對象上調用。 在這種情況下, Console
但此類沒有請求的方法,也不是 Class 的子Class
。 JVM 打算在這里做什么/像 Hotspot 這樣的 JVM 實現是如何做到這一點的。 他們只是在指令期間將方法放到Console
類中,還是以某種方式將類Class
注入到Console
中,或者這里可能發生了一些我遺漏的完全不同的事情?
無論如何,感謝您花時間閱讀我的問題!
我希望你有一個美好的一天:)
我想過做以下事情:
在指令期間將方法添加到Console
類。 這不起作用,因為該方法需要來自類Class
的字段。
實際上只是在動態創建的Class
實例上調用該方法,但我認為這些看起來很奇怪。
這可能是一個非常特殊的情況,因為我認為它與Console
試圖與其ClassLoader
交互有關。 如果這是一個特殊情況並且這種情況發生的次數不多:也許 JVM 只是做了一些幕后魔術,比如在加載后為每個類分配一個Class
實例,然后僅用於此目的。 對我來說也很奇怪。
你寫了
已經可以看出此
invokevirtual
指令與上述指令之間的區別:javap
將類名附加在方法名之前,因為該函數是在類Class
中找到的,而不是在Console
中。
但在這方面沒有區別。 javap
在這兩種情況下都包含了類名。 對於println
方法,它是類java/io/PrintStream
。
對於普通的 Java 代碼, ldc
指令可以加載原始值或String
或Class
類型的對象。 我們可以用
public class Main {
static {
Console.class.desiredAssertionStatus();
"hello".toString();
}
public static void main(String[] args) {
showBytecode();
}
private static void showBytecode() {
ToolProvider.findFirst("javap")
.ifPresent(p -> p.run(System.out, System.err, "-c", "Main"));
}
private Main() {}
}
Compiled from "Main.java"
public class Main {
public static void main(java.lang.String[]);
Code:
0: invokestatic #1 // Method showBytecode:()V
3: return
static {};
Code:
0: ldc #13 // class java/io/Console
2: invokevirtual #14 // Method java/lang/Class.desiredAssertionStatus:()Z
5: pop
6: ldc #15 // String hello
8: invokevirtual #16 // Method java/lang/String.toString:()Ljava/lang/String;
11: pop
12: return
}
我們可以看到javap
總是在ldc
指令之后打印實際值,所以在這個例子中,“class java/io/Console”和“String hello”。
ldc
指令還可以加載其他類型的對象,但沒有與這些用例等效的普通 Java 語言。
desiredAssertionStatus()
查詢通常用於實現assert
語句支持,例如。
public class Main {
public static void main(String[] args) {
assert "foo".length() == 3;
showBytecode();
}
private static void showBytecode() {
ToolProvider.findFirst("javap")
.ifPresent(p -> p.run(System.out, System.err, "-c", "Main"));
}
private Main() {}
}
Compiled from "Main.java"
public class Main {
static final boolean $assertionsDisabled;
public static void main(java.lang.String[]);
Code:
0: getstatic #1 // Field $assertionsDisabled:Z
3: ifne 23
6: ldc #2 // String foo
8: invokevirtual #3 // Method java/lang/String.length:()I
11: iconst_3
12: if_icmpeq 23
15: new #4 // class java/lang/AssertionError
18: dup
19: invokespecial #5 // Method java/lang/AssertionError."<init>":()V
22: athrow
23: invokestatic #6 // Method showBytecode:()V
26: return
static {};
Code:
0: ldc #18 // class Main
2: invokevirtual #19 // Method java/lang/Class.desiredAssertionStatus:()Z
5: ifne 12
8: iconst_1
9: goto 13
12: iconst_0
13: putstatic #1 // Field $assertionsDisabled:Z
16: return
}
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.