How Java Works (Compilation & Execution Flow)
Understanding how Java works internally explains platform independence, performance, and many runtime errors seen in real projects. Java follows a two-step process: compilation and execution.
High-Level Flow
- Write Java source code (.java)
- Compile source code into bytecode (.class)
- Load bytecode into JVM
- Verify bytecode
- Execute bytecode using interpreter and JIT compiler
- JVM interacts with OS and hardware
Step 1: Writing Java Source Code
- Developer writes code in a text file with .java extension
- Code follows Java syntax rules
- Code is platform dependent at this stage
Example:
public class Hello {
public static void main(String[] args) {
System.out.println("Hello Java");
}
}
Step 2: Compilation (javac)
- Java compiler (javac) compiles .java file
- Checks:
- Syntax errors
- Type errors
- Missing semicolons, brackets, etc.
Output:
- .class file (bytecode)
Key Point:
Bytecode is platform independent.
Step 3: Class Loading (Class Loader)
Once execution starts using:
java Hello
The Class Loader Subsystem loads required classes into memory.
Types of Class Loaders
- Bootstrap Class Loader – Loads core Java classes
- Extension Class Loader – Loads extension classes
- Application Class Loader – Loads user-defined classes
Why it matters:
Ensures correct and secure class loading.
Step 4: Bytecode Verification
- JVM verifies bytecode before execution
- Checks:
- No illegal memory access
- Stack integrity
- Valid instructions
Why it matters:
Prevents malicious or corrupted code from running.
Step 5: Execution by JVM
Execution is handled by the Execution Engine, which has two parts:
a) Interpreter
- Reads bytecode line by line
- Converts it into machine instructions
- Slower for repeated execution
b) JIT (Just-In-Time) Compiler
- Identifies frequently executed code (hot spots)
- Converts bytecode into native machine code
- Caches compiled code for reuse
Why it matters:
Improves performance significantly.
Step 6: Runtime Memory Management
During execution, JVM manages memory using runtime areas:
- Heap – Objects
- Stack – Method calls, local variables
- Method Area – Class metadata
- PC Register – Current instruction
- Native Method Stack – Native code
Garbage Collector (GC):
- Automatically removes unused objects from heap
Step 7: Interaction with OS & Hardware
- JVM communicates with OS
- OS communicates with hardware
- Same bytecode behaves consistently across platforms
This is how Java achieves:
Write Once, Run Anywhere
Visual Flow (Conceptual)
Hello.java
↓ (javac)
Hello.class (Bytecode)
↓
Class Loader
↓
Bytecode Verifier
↓
Execution Engine
├─ Interpreter
└─ JIT Compiler
↓
Operating System
↓
Hardware
Summary Table
| Stage | Responsibility | Output |
|---|---|---|
| Source Code | Developer writes code | .java |
| Compilation | Syntax & type check | .class |
| Class Loading | Loads classes | Memory |
| Verification | Security check | Verified bytecode |
| Execution | Interpreter + JIT | Machine code |
| Memory Mgmt | GC & runtime areas | Optimized execution |
Common Mistakes by Beginners
- Thinking Java is fully interpreted
- Believing bytecode runs directly on OS
- Ignoring JIT compiler role
- Confusing compilation errors with runtime errors
- Not understanding GC behavior
Interview-Ready Answers
Short answer:
Java works by compiling source code into bytecode, which is executed by the JVM using an interpreter and JIT compiler.
Detailed answer:
Java source code is compiled by javac into platform-independent bytecode. The JVM loads, verifies, and executes this bytecode using an interpreter and JIT compiler, while managing memory and interacting with the operating system.
Key Takeaway
Java separates compilation and execution, allowing bytecode to run on any system with a JVM, delivering portability, security, and performance.