【问题标题】:What does JVM interpreter (NOT the JIT compiler) actually do?JVM 解释器(不是 JIT 编译器)实际上是做什么的?
【发布时间】:2019-04-30 14:49:23
【问题描述】:

请注意,我的问题是关于 JVM 解释器,而不是 JIT 编译器。 JIT 编译器将 java 字节码转换为本机机器码。因此,这必须意味着 JVM 中的解释器不会将字节码转换为机器码。因此问题是:本质上,解释器是做什么的?如果有人可以用一个相当于 1+1 = 2 的字节码的简单示例来帮助我回答这个问题,即解释器在执行此添加操作方面做了什么? (我的隐含问题是,如果解释器不翻译成机器码,然后由哪个 CPU 执行 ADD 操作,那么这个操作是如何执行的?实际执行了哪些机器码来支持这个 ADD 操作?)

【问题讨论】:

  • 实际的机器码是解释器的一部分。想想一个包含 switch 语句的循环,每个现有的字节码指令都有一个案例,在那里执行操作或调用子程序。

标签: jvm interpreter


【解决方案1】:

表达式1+1 将编译为以下字节码:

iconst_1
iconst_1
add

(实际上,它只会编译为 iconst_2,因为 Java 编译器执行常量折叠,但为了回答的目的,我们忽略它。)

所以要确切了解解释器对这些指令做了什么,我们应该查看its source codeconst_1add的相关部分分别以line 983line 1221开头,我们来看看:

#define OPC_CONST_n(opcode, const_type, value)                          \
      CASE(opcode):                                                     \
          SET_STACK_ ## const_type(value, 0);                           \
          UPDATE_PC_AND_TOS_AND_CONTINUE(1, 1);

          OPC_CONST_n(_iconst_m1,   INT,       -1);
          OPC_CONST_n(_iconst_0,    INT,        0);
          OPC_CONST_n(_iconst_1,    INT,        1);
          // goes on for several other constants

//...
#define OPC_INT_BINARY(opcname, opname, test)                           \
      CASE(_i##opcname):                                                \
          if (test && (STACK_INT(-1) == 0)) {                           \
              VM_JAVA_ERROR(vmSymbols::java_lang_ArithmeticException(), \
                            "/ by zero", note_div0Check_trap);          \
          }                                                             \
          SET_STACK_INT(VMint##opname(STACK_INT(-2),                    \
                                      STACK_INT(-1)),                   \
                                      -2);                              \
          UPDATE_PC_AND_TOS_AND_CONTINUE(1, -1);                        \
          // and then the same thing for longs instead of ints

      OPC_INT_BINARY(add, Add, 0);
      // other operators

整个事情都在一个检查当前指令操作码的 switch 语句中。

如果我们扩展宏魔法,用一个极其简化的模板替换周围的代码,并做一些简化的假设(比如堆栈只包含ints),我们最终会得到这样的结果:

enum OpCode {
  _iconst_1, _iadd
};

// ...
int* stack = new int[calculate_maximum_stack_size()];
size_t top_of_stack = 0;
size_t program_counter = 0;
while(program_counter < program_size) {
  switch(opcodes[program_counter]) {
    case _iconst_1:
      // SET_STACK_INT(1, 0);
      stack[top_of_stack] = 1;
      // UPDATE_PC_AND_TOS_AND_CONTINUE(1, 1);
      program_counter += 1;
      top_of_stack += 1;
      break;

    case _iadd:
      // SET_STACK_INT(VMintAdd(STACK_INT(-2), STACK_INT(-1)), -2);
      stack[top_of_stack - 2] = stack[top_of_stack - 1] + stack[top_of_stack - 2];
      // UPDATE_PC_AND_TOS_AND_CONTINUE(1, -1);
      program_counter += 1;
      top_of_stack += -1;
      break;
}

所以对于1+1,操作顺序是:

stack[0] = 1;
stack[1] = 1;
stack[0] = stack[1] + stack[0];

top_of_stack 将是 1,因此我们将以包含值 2 作为其唯一元素的堆栈结束。

【讨论】:

    猜你喜欢
    • 2011-07-01
    • 2017-05-20
    • 2020-10-27
    • 2020-06-20
    • 1970-01-01
    • 2019-03-01
    • 2011-03-21
    • 1970-01-01
    相关资源
    最近更新 更多