Concepts / Java bytecode

Java bytecode

Just-in-time compilation translates Java bytecode into native machine language during program execution.

  • Programming

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.

Java bytecodeprogram being runInterpreter executesbytecodedirect executionPart used repeatedlymany executionsPart translated tonative codeduring executionNative machine coderunsfaster repeated executionloaded and runparts executedyeslater executions
What steps does the bytecode go through from loading to running, and where does the JIT compiler fit into that sequence?

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

Definition

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.

Read next bytecodeinstructionTranslate section tonative codeInterpreter performsits actionRun native machinecodeRepeat for everyexecutionReuse translated codelaterthenthenthenthen
How does the execution path differ when each bytecode instruction is interpreted directly versus when a section is compiled to native code first and then run?
Key point

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.

Run 1: interpretsectiontranslation happens duringthis runTranslate sectionone-time costRuns 2 onward: nativecodetranslated result reusedOverall time reducedwhen repetition is highnextnextaccumulates
Why does translating a frequently executed section once, then reusing the native version, reduce total execution time across repeated runs?

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

Worked example

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.

  1. 1
    Interpretation 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.

  2. 2
    Just-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.

  3. 3
    Reuse 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.

  4. 4
    Overall 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.

Result

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 like

    Concluding that a Java program written as bytecode must always be slower than the same work compiled into native machine language.

    Why it fails

    The 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 instead

    Say 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 like

    Believing the bytecode is fully converted to native machine language in a separate step before any code runs.

    Why it fails

    The source describes the translation as happening during program execution.

    Do this instead

    Describe 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 like

    Assuming a section that runs once benefits as much as a section that runs thousands of times.

    Why it fails

    The source says the greatest benefit occurs when a program part is executed many times.

    Do this instead

    Expect 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?

  • Yes, the section will run much faster because it is translated

  • No, the benefit is small because the translated result is not reused much

Reveal answer
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.

MEDIUM

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.