Java bytecode
Just-in-time compilation translates Java bytecode into native machine language during program execution.
The Speed Question Behind Java
A common early assumption about Java goes like this: if a program is written as Java bytecode and an interpreter executes that bytecode, the program can never be as fast as a program that has already been compiled into the computer's native machine language. The assumption points at a real performance concern. Bytecode is not the machine's own language, and an interpreter has to work through it step by step. But the assumption leaves out an important part of how Java execution can work, and this article follows one program as it runs to show what that missing part is.
Following One Program Through Its Stages
Imagine a Java program that has been turned into bytecode. As it runs, the bytecode is carried out by an interpreter, which executes the bytecode directly. Just-in-time compilation adds a further step inside this process: parts of the program can be translated into native machine language while the program is running. Those translated parts can then run from native machine language instead of being interpreted again. The diagram below shows where that translation step sits in the sequence from bytecode to running code.
Notice that the translation is not a separate build step that happens before the program starts. The source describes it as happening while the program is running. Interpretation and translation are both part of the same run, which is why the rest of this article compares them directly.
Interpretation Versus Just-in-Time Compilation
Just-in-time compilation translates Java bytecode into native machine language during program execution.
The key contrast is what happens to each bytecode instruction. With direct interpretation, the interpreter handles bytecode instructions one by one as the program runs. With just-in-time compilation, a section of the program can be translated into native machine language, so that later runs of that section use the translated form. The source notes that interpreted bytecode can run slower than native machine language, but translated parts can execute much more quickly than they could through interpretation. The comparison below lays out both execution paths side by side.
Direct interpretation keeps working through the bytecode each time it is needed. Just-in-time compilation spends effort once on translating a part of the program, and the translated result can then be used for later execution of that part.
Why Repetition Makes Translation Pay Off
The benefit of just-in-time compilation comes from repetition. A given part of a program is often executed many times as the program runs. If that part is translated into native machine language during execution, later execution of the translated part can be faster than repeatedly interpreting it. The translation itself takes place while the program runs, but the translated result can improve overall execution time when the relevant part is used repeatedly. The source states that the greatest benefit occurs when a program part is executed many times.
The diagram shows the trade-off. Translation has a cost that is paid once. The reuse afterward can be repeated many times, so the saving can add up. This is why a section that runs only once gains little from translation, while a section that runs many times is a strong candidate for it.
Tracing a Repeated Section
A section that runs many times
A program contains a section that is executed 1,000 times while the program runs. Consider how interpretation and just-in-time compilation handle that section. This is an illustrative scenario created for this article; the source does not give specific timings or counts.
- 1Interpretation only
Each of the 1,000 executions is carried out by the interpreter working through the bytecode. The interpreter handles the section again every time it is needed.
- 2Just-in-time compilation
During execution, the section is translated into native machine language. This translation is done while the program runs, and it is done for the section that is used repeatedly.
- 3Reuse of the translated part
The remaining executions of that section use the native machine language version. The source says translated parts can execute much more quickly than they could through interpretation.
- 4Overall effect
Because the section is used many times, the one-time cost of translation is spread across many faster runs. The overall execution time can improve significantly.
The benefit depends on repetition. The more often the translated section is used, the more the faster native execution can contribute to the program's overall execution time.
Correcting the Always-Slower Assumption
The early assumption is a useful starting point, but it is stated too strongly. The more precise version is that interpreted Java bytecode can run slower than native machine language, while just-in-time compilation can translate parts of the bytecode into native machine language during execution. Those parts can then run much faster than they would through interpretation.
Common mistake Assuming bytecode programs can never run as quickly as native-compiled programs
What it looks likeConcluding that a Java program written as bytecode must always be slower than the same work compiled into native machine language.
Why it failsThe source says just-in-time compilation can translate parts of the bytecode into native machine language during execution, and those parts can then execute much more quickly than through interpretation.
Do this insteadSay that interpreted bytecode can be slower than native machine language, and that just-in-time compilation can speed up translated parts of the program.
Common mistake Thinking the translation happens before the program starts
What it looks likeBelieving the bytecode is fully converted to native machine language in a separate step before any code runs.
Why it failsThe source describes the translation as happening during program execution.
Do this insteadDescribe translation as something that occurs while the program is running, applied to parts of the program.
Common mistake Expecting translation to help every section equally
What it looks likeAssuming a section that runs once benefits as much as a section that runs thousands of times.
Why it failsThe source says the greatest benefit occurs when a program part is executed many times.
Do this insteadExpect the largest gains for parts of the program that are executed repeatedly.
Checking Your Reasoning
What do you think happens?
A section of a program runs only once. Will just-in-time compilation of that section help overall execution time much?
Reveal answer
No, the benefit is small.
The source says the benefit of just-in-time compilation comes from repetition, and the greatest benefit occurs when a program part is executed many times. A section that runs only once does not get many faster executions after its translation.
Explain, in your own words, why an interpreted bytecode program can be slower than a native-compiled one and yet still have its overall execution time improved by just-in-time compilation. Refer to the role of repeated execution in your answer.
Hints
- Think about what is paid once and what is reused.
- Consider which parts of the program benefit most from translation.
Key Takeaways
- Java bytecode is executed by an interpreter, and just-in-time compilation can translate parts of it into native machine language during execution.
- Direct interpretation handles each bytecode instruction as it is needed, while just-in-time compilation produces a translated version of a section that can be reused.
- The benefit of translation comes from repetition: a section executed many times can gain from being run as native machine language after translation.
- Interpreted bytecode can be slower than native machine language, but the always-slower assumption is incorrect because translated parts can execute much more quickly.