11

两条指令都使用静态而不是动态调度。似乎唯一的实质性区别是,invokespecial它的第一个参数总是有一个对象,该对象是分派方法所属的类的实例。但是,invokespecial实际上并没有将对象放在那里;编译器负责通过在发出之前发出适当的堆栈操作序列来实现这一点invokespecial。所以替换invokespecialinvokestatic应该影响运行时堆栈/堆的操作方式——尽管我希望它会导致VerifyError违反规范。

我很好奇制作两条不同的指令背后的可能原因,这些指令基本上做同样的事情。我查看了 OpenJDK 解释器的源代码,它看起来invokespecialinvokestatic处理方式几乎相同。有两个单独的指令是否有助于 JIT 编译器更好地优化代码,或者它是否有助于类文件验证器更有效地证明某些安全属性?或者这只是 JVM 设计中的一个怪癖?

4

3 回答 3

3

免责声明:很难确定,因为我从未阅读过明确的 Oracle 声明,但我认为这就是原因:

当您查看 Java 字节码时,您可能会就其他指令提出相同的问题。为什么验证者会在将两个ints 压入堆栈并立即将它们视为一个时阻止您long?(试试看,它会阻止你。)你可以争辩说,通过允许这样做,你可以用更小的指令集表达相同的逻辑。(为了进一步说明这个论点,一个字节不能表达太多的指令,因此 Java 字节码集应尽可能减少。)

当然,理论上您不需要字节码指令来将ints 和s 推入堆栈,而且long您不需要两条指令来表达方法调用,这一点是正确的。方法由其方法描述符(名称和原始参数类型)唯一标识,您不能在同一类中定义具有相同描述的静态和非静态方法。为了验证字节码,Java 编译器必须检查目标方法是否仍然存在。INVOKESPECIALINVOKESTATICstatic

备注:这与 v6ak 的答案相矛盾。但是,非静态方法的方法描述符不会更改为包含对this.getClass(). INVOKESMART因此,Java 运行时总是可以从假设指令的方法描述符中推断出适当的方法绑定。请参阅 JVMS §4.3.3。

理论就这么多。但是,两种调用类型表达的意图是完全不同的。请记住,Java 字节码也应该被javac以外的其他工具用来创建 JVM 应用程序。使用字节码,这些工具生成的东西比 Java 源代码更类似于机器代码。但它仍然是相当高的水平。例如,仍然会验证字节码,并且在编译为机器码时会自动优化字节码。但是,字节码是一种抽象,它故意包含一些冗余以使含义的字节码更明确。就像 Java 语言为相似的事物使用不同的名称以使语言更具可读性一样,字节码指令集也包含一些冗余。另一个好处是,验证和字节码解释/编译可以加快速度,因为方法的调用类型并不总是需要推断,而是在字节码中明确说明。这是可取的,因为验证、解释和编译是在运行时完成的。

作为最后的轶事,我应该提到在 Java 5 之前<clinit>没有标记类的静态初始化static程序。在这种情况下,静态调用也可以通过方法的名称来推断,但这会导致更多的运行时开销。

于 2013-11-29T23:56:49.630 回答
2

有以下定义:


存在显着差异。假设我们要设计一个invokesmart指令,它会在inkovestatic和之间进行巧妙的选择invokespecial

首先,区分静态调用和虚拟调用不会有问题,因为我们不能有两个具有相同名称、相同参数类型和相同返回类型的方法,即使一个是静态的,第二个是虚拟的。JVM 不允许这样做(出于一个奇怪的原因)。感谢 raphw 注意到这一点。

首先,invokesmart 是什么foo/Bar.baz(I)I意思?这可能意味着:

  • 从操作数堆栈foo.Bar.baz消耗并添加另一个的静态方法调用。intint// (int) -> (int)
  • 一个实例方法调用foo.Bar.baz,它消耗foo.Barint来自操作数堆栈并添加int.// (foo.Bar, int) -> (int)

你会如何选择他们?两种方法都可能存在。

我们可以尝试通过要求foo/Bar.baz(Lfoo/Bar;I)静态调用来解决它。但是,我们可能同时拥有public static int baz(Bar, int)public int baz(int)


我们可以说这无关紧要并且可能禁用这种情况。(我认为这不是一个好主意,只是想象一下。)这意味着什么?

  • 如果该方法是静态的,则可能没有其他限制。另一方面,如果方法不是静态的,则有一些限制:“最后,如果解析的方法是受保护的(第 4.6 节),并且它要么是当前类的成员,要么是当前类的超类的成员类,那么 objectref 的类必须是当前类或当前类的子类。”
  • 还有一些进一步的区别,请参阅关于ACC_SUPER.
  • 这意味着必须在字节码验证之前加载所有引用的类。我希望现在没有必要,但我不是 100% 确定。

因此,这将意味着非常不一致的行为。

于 2013-01-06T16:16:54.563 回答
0

For getting a clear practical idea about these codes, you need to add eclipse plugin for ASM in your eclipse IDE and find out the bytecode generated for your sample Hello World program which you created.

于 2013-10-09T12:44:49.843 回答