Source → bytecode → JVM in Java
javac compiles .java to .class bytecode; the JVM loads, verifies, interprets and JIT-compiles it.
Step 1: javac compiles
You write Hello.java. Running javac Hello.java checks your code and writes Hello.class, which contains bytecode: compact instructions for the JVM. Not source code, and not native machine code.
$ javac Hello.java
$ ls
Hello.class Hello.javaStep 2: load and verify
When you run java Hello, the JVM loads the class. Then the bytecode verifier checks that it follows the platform's safety rules, like type safety and correct stack use. A tampered or corrupt .class file is rejected before it can run.
Step 3: interpret, then JIT
The JVM starts by interpreting bytecode, one instruction at a time. Meanwhile it counts which methods run a lot. That hot code gets compiled by the JIT (just-in-time) compiler into optimized native machine code, while the program keeps running.
Put it in order
Which order is correct?
Write .java, javac makes .class, JVM runs itWrite .class, javac makes .java, JVM runs itWrite .java, JVM runs it, javac saves .class
Show the answer
Source is compiled first, then the JVM executes the resulting bytecode. javac never runs your program, and the JVM never reads your .java file.
Why compile while running?
Why not compile everything to native code up front? Why wait until the program is running?
Think about it, then reveal the answer
Because at runtime the JVM can watch real behavior: which methods are hot, which branches are taken, which types actually show up. It optimizes for how the code is really used, which can beat guessing in advance.
The warm-up trap
Time your code right after startup and you're measuring the slow, interpreted phase. Code gets fast only after it's hot and JIT-compiled: the warm-up effect. Serious benchmark tools such as JMH run warm-up rounds before measuring.
In real projects
Many teams send a few warm-up requests to a freshly started service before giving it real traffic, so the hot paths are already compiled. And "why does a Java app get faster after a while?" is a classic interview question. Answer: the JIT.
Key takeaways
- javac: .java source → .class bytecode
- The JVM loads and verifies classes before running them
- The JIT compiler turns hot bytecode into native code at runtime
💡 javac translates a recipe into a universal shorthand; the JVM is the chef who reads it and gets faster at dishes it cooks often.
Every .class file starts with the same four bytes: 0xCAFEBABE. It's a "magic number" the JVM checks to recognize a class file.
Practice questions
Which order is correct?
- Write .class → javac → .java → JVM runs it
- Write .java → javac → .class → JVM runs it
- Write .java → JVM → .class → javac runs it
- Write .java → JVM runs it → javac saves .class
Check your answer
Write .java → javac → .class → JVM runs it. Source is compiled first, then the resulting bytecode is executed by the JVM.
What does the JIT (just-in-time) compiler do?
- Converts .java files into .class files
- Checks the source code for syntax errors
- Compiles frequently executed bytecode into native machine code while the program runs
- Removes unused classes from the .jar file
Check your answer
Compiles frequently executed bytecode into native machine code while the program runs. The JVM starts by interpreting bytecode, watches which methods run a lot, and compiles those to optimized native code.