How does an interpreter interpret the code?

后端 未结 4 618
有刺的猬
有刺的猬 2020-12-23 22:01

For simplicity imagine this scenario, we have a 2-bit computer, which has a pair of 2 bit registers called r1 and r2 and only works with immediate addressing.

Lets

4条回答
  •  自闭症患者
    2020-12-23 22:15

    Simplifying, interpreter is a infinite loop with a giant switch inside. It reads Java byte code (or some internal representation) and emulates a CPU executing it. This way the real CPU executes the interpreter code, which emulates the virtual CPU. This is painfully slow. Single virtual instruction adding two numbers requires three function calls and many other operations. Single virtual instruction takes a couple of real instructions to execute. This is also less memory efficient as you have both real and emulated stack, registers and instruction pointers.

    while(true) {
        Operation op = methodByteCode.get(instructionPointer);
        switch(op) {
            case ADD:
                stack.pushInt(stack.popInt() + stack.popInt())
                instructionPointer++;
                break;
            case STORE:
                memory.set(stack.popInt(), stack.popInt())
                instructionPointer++;
                break;
            ...
    
        }
    }
    

    When some method is interpreted multiple times, JIT compiler kicks in. It will read all virtual instructions and generate one or more native instructions which does the same. Here I'm generating string with text assembly which would require additional assembly to native binary conversions.

    for(Operation op : methodByteCode) {
        switch(op) {
            case ADD:
                compiledCode += "popi r1"
                compiledCode += "popi r2"
                compiledCode += "addi r1, r2, r3"
                compiledCode += "pushi r3"
                break;
            case STORE:
                compiledCode += "popi r1"
                compiledCode += "storei r1"
                break;
            ...
    
        }
    }
    

    After native code is generated, JVM will copy it somewhere, mark this region as executable and instruct the interpreter to invoke it instead of interpreting byte code next time this method is invoked. Single virtual instruction might still take more than one native instruction but this will be nearly as fast as ahead of time compilation to native code (like in C or C++). Compilation is usually much slower than interpreting, but has to be done only once and only for chosen methods.

提交回复
热议问题