JVM如何实际读取和运行字节码?一个巨大的转换案例陈述?

时间:2018-04-18 01:51:47

标签: java jvm bytecode jit bytecode-manipulation

或者,在JVM的内部循环中实际发生了什么?

我认为这是一个特定于实现的问题,但JVM实际上是如何运行字节码的?最天真的方法可能包括一个巨大的开关案例 - 使用C语法:

while(bytecode = *instruction_pointer++){
    switch(bytecode){
        case AALOAD:
          //...
        case AASTORE:
          //...
        case ACONST_NULL:
          //...
        //... 256 cases ...
    }
}

显然,这是非常缓慢的,并且不会远远超出我们看到Java在现实世界中达到的速度。

另一种方法可能是将更长的“运行”字节码编译成机器代码,然后JMP到这个机器代码,JMP在完成后返回JVM。这似乎可能更有效,但实际上是否有可能将字节码有效地转换为等效的机器语言指令(特别是跨平台!)是可疑的。

这显然是一个可以通过多篇博士论文长篇论文来回答的问题,但任何人都可以对字节码的实际处理方式提供一般性的高级概念吗?或者提一下在真正的JVM中使用的一些巧妙的技巧?

提前致谢! JIT非常酷。

2 个答案:

答案 0 :(得分:2)

答案实际上是"以上所有"。

第一次运行方法时,它可能是解释。这意味着它的处理方式与您的switch语句示例非常相似。

但是,如果JVM发现自己多次解释一个方法,它将字节码编译成机器代码。正如您所说,它将在下次运行该方法时直接跳转到该机器代码。

JVM第一次编译方法时,它可能是一个非常简单的编译,可能无法很好地优化。我们的想法是快速完成它,结果是一种比解释版本执行速度快得多的方法,但仍然没有那么快。然而,JVM将检测该方法,然后观察它执行,因为它被调用了更多次并收集统计信息。

如果方法运行很多,那么这些统计信息用于指导更广泛优化的编译。这需要相当长的时间,但是在编译运行时程序实际上并不需要停止,因此您并不会注意到。当它完成后,你有一个很好的快速编译版本的方法,它就像你从专用的编译传递中获得的那样好或更好。

请参阅"配置文件引导优化":https://en.wikipedia.org/wiki/Profile-guided_optimization

答案 1 :(得分:0)

找到源代码!将此作为答案添加但不将其标记为已接受,因为除此之外可能有更多可能有趣的答案。

https://github.com/openjdk-mirror/jdk7u-hotspot/blob/50bdefc3afe944ca74c3093e7448d6b889cd20d1/src/share/vm/interpreter/bytecodeInterpreter.cpp#L913

switch (opcode)
{
    CASE(_nop):
        UPDATE_PC_AND_CONTINUE(1);
    // ... et cetera ...
}