0

Or, what actually goes on in the inner loop of the JVM?

I suppose this is a rather implementation-specific question, but how actually does the JVM run bytecodes? The most naive method might consist of a giant switch case - using C syntax:

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

Obviously, this is exceedingly slow and would not be remotely capable of the speeds we see Java reaching in the real world.

Another way might be to compile longer "runs" of bytecodes into machine code, then JMPing to this machine code, with a JMP back to the JVM at the end after it's done. This seems possibly more efficient, but whether it's actually possible to efficiently translate bytecodes into equivalent machine language instructions (especially cross-platform!) is dubious.

This is obviously a question that could be answered with multiple PhD-thesis-long papers, but could anyone give a general, high-level idea of how the bytecodes are actually processed? Or mention a few clever tricks that are used in the real JVM?

Thanks in advance! JITs are amazingly cool.

oink
  • 1,443
  • 2
  • 14
  • 23
  • Have you considered looking up the code? – shmosel Apr 18 '18 at 01:56
  • I have; I haven't yet comprehended it enough yet to find the "inner loop". I have found the inner loop in this toy project: https://github.com/zxh0/jvm.go - it does indeed seem to be the equivalent of loop{ read; execute }, though of course this project is suuuuper inefficient compared to, say, the Sun JVM. – oink Apr 18 '18 at 02:01
  • 1
    There are a number of "real JVMs". If you want to know about the Sun/Oracle one, it's named HotSpot, and it does a combination of interpretation (basically like what you showed) and on-the-fly compilation to native instructions. – chrylis -cautiouslyoptimistic- Apr 18 '18 at 02:04
  • May be [this video](https://vimeo.com/186425635) will help – apangin Apr 18 '18 at 02:08
  • @chrylis Found HotSpot on Github! Seems that, indeed, there is a switch case deep in there. https://github.com/openjdk-mirror/jdk7u-hotspot/blob/50bdefc3afe944ca74c3093e7448d6b889cd20d1/src/share/vm/interpreter/bytecodeInterpreter.cpp#L913 – oink Apr 18 '18 at 02:16
  • And thanks so much @apangin! Will watch :D – oink Apr 18 '18 at 02:21

2 Answers2

2

The answer is really "all of the above".

The first time you run a method, it may be interpreted. That means that it's processed much like your switch statement example.

If the JVM finds itself interpreting a method more than a couple times, however, it will compile the bytecode into machine code. As you say it will then jump into that machine code directly the next time it has to run that method.

The first time the JVM compiles a method, it can be a pretty simple compilation and might not optimize very well. The idea is to get it done fast, and the result is a method that executes much faster than the interpreted version, but still not nearly as fast as it could be. The JVM will instrument the method, however, and then watch it execute as it's called more times and gather statistics.

If the method runs a lot, then these statistics are used to guide a more extensively optimized compilation. This takes a fair amount of time, but the program doesn't actually have to stop while this compilation runs, so you don't really notice. When it's done you have a nice fast compiled version of the method that's pretty much as good or better as you'd get out of a dedicated compilation pass.

See "Profile Guided Optimization": https://en.wikipedia.org/wiki/Profile-guided_optimization

Matt Timmermans
  • 53,709
  • 3
  • 46
  • 87
  • That's actually amazingly cool - it actually instruments it every time it runs so that it can change the way it compiles it?? The amount of complexity required to do that must be immense, but I think it kind of makes sense now that it can occasionally be as fast or faster than good old C. – oink Apr 18 '18 at 02:18
0

Found the source code! Going to add this as an answer but not mark it as accepted, since there's probably a lot more potential for interesting answers beyond this.

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 ...
}
oink
  • 1,443
  • 2
  • 14
  • 23