A process is an independent program in execution, created and managed by the operating system. When you run a Java program, the OS:
- Loads the JVM executable
- Creates a new process
- Allocates isolated memory regions (stack, heap, code, data)
- Starts the program with its own JVM instance
- Each process has its own address space
- One crashed process does not affect others
- No memory is shared unless explicitly done using IPC
- Every Java application you run → 1 dedicated OS process
Running a Java file involves several components:
javac → compile source to bytecode (.class)
java → launch JVM process → load class → interpret/JIT → execute
javac MyProgram.java- Converts
.java→.class(bytecode) - Bytecode is platform-independent
java MyProgramWhen executed:
-
OS creates new process
-
JVM instance is created inside that process
-
JVM allocates:
- Heap
- Method Area (Metaspace)
- Code Cache for JIT
- Thread stacks
-
Bootstraps necessary system classes (String, System, Thread)
The Class Loader Subsystem loads:
MyProgram.class- Dependent classes
- JDK library classes
It performs:
- Loading
- Verification
- Preparation
- Resolution
- Initialization
The Execution Engine handles the program using:
- Reads and executes bytecode instruction-by-instruction
- Fast startup but slow for repeated code
- Detects frequently executed ("hot") code
- Compiles bytecode → native machine code
- Stores compiled code in Code Cache
- JVM switches execution to native code → massive performance boost
This hybrid model ensures:
- Fast startup
- High long-term performance
Java allows you to control heap allocation.
-Xms<size>
-Xmx<size>
Example:
java -Xms512m -Xmx4g MyProgramIf memory exceeds -Xmx → JVM throws:
java.lang.OutOfMemoryError: Java heap space
| Region | Purpose |
|---|---|
| Heap | Objects, arrays |
| Metaspace | Class metadata |
| Thread Stacks | Local variables, frames |
| Code Cache | JIT compiled machine code |
| Native Memory | Libraries, buffers |
Regardless of language, OS allocates typical memory segments:
+-------------------------------+
| Code Segment (Text) |
| - JVM executable machine code |
+-------------------------------+
| Data Segment |
| - Global variables |
| - Static data |
+-------------------------------+
| Heap |
| - Runtime allocated objects |
+-------------------------------+
| Stack (per thread) |
| - Local vars, frames |
+-------------------------------+
| Registers |
| - PC, SP, general regs |
+-------------------------------+
JVM has its own logical memory structure:
+--------------------------------------+
| Method Area (Metaspace) |
+--------------------------------------+
| Heap (Young + Old Gen) |
+--------------------------------------+
| JVM Stack (per thread) |
+--------------------------------------+
| PC Register (per thread) |
+--------------------------------------+
| Native Method Stack (per thread) |
+--------------------------------------+
Stores:
- Class structure
- Static variables
- Constant pool
- Method bytecode
- Reflection data
Java 8+ uses Metaspace (native memory), not PermGen.
Largest memory area. Stores:
- Objects
- Arrays
- Class instances
Divided into:
| Area | Purpose |
|---|---|
| Eden | New objects |
| Survivor S0/S1 | Objects that survived Minor GC |
| Old Gen | Long-lived objects |
Each thread gets its own stack.
Holds:
- Local variables
- Method call frames
- Return addresses
- Primitive variables
- References to objects (actual objects in heap)
Stack memory is limited → recursion can cause:
java.lang.StackOverflowError
Each Java thread has a separate Program Counter storing the next instruction to execute.
PC is critical for:
- Multithreading
- Context switching
- Keeping track of execution flow
Used for JNI (Java Native Interface):
- C/C++ code execution
- Native library calls
A thread is the smallest execution unit inside a process.
A process can have multiple threads, all sharing:
- Heap
- Method Area
- Code Cache
But having separate:
- Stack
- PC register
- Native stack
NEW → RUNNABLE → RUNNING → BLOCKED/WAITING → TERMINATED
| Region | Purpose |
|---|---|
| Java Stack | Method frames, locals |
| PC Register | Next instruction |
| Native Stack | JNI calls |
Context switching allows CPU to switch between threads.
When switching threads:
- Program Counter (PC)
- Registers
- Stack pointer
- Thread-specific state
Context switching is:
- CPU intensive
- Expensive for large thread counts
- Can slow down program if too many threads are created
Thus ExecutorService thread pools are preferred.
1. Programmer runs "java MyApp"
2. OS creates a new process
3. JVM instance starts inside that process
4. Class loaders load necessary classes
5. JVM creates main thread
6. Execution Engine interprets bytecode
7. JIT compiles hot code → machine code
8. Threads execute instructions
9. GC manages heap memory
10. Program ends → JVM shuts down → process destroyed
JVM maintains a special memory where native machine code generated by JIT is stored.
Benefits:
- Faster repeated execution
- Reduced interpretation
- Native-level performance
Creating a thread consumes:
- Memory (stack)
- Scheduling overhead
- OS kernel resources
Hence Java frameworks use:
- Cached thread pools
- Fixed thread pools
- ForkJoinPool
More threads may:
- Increase GC pauses
- Put pressure on heap
- Cause race conditions if not synchronized properly
Process: independent execution unit with its own memory Thread: lightweight execution unit inside a process, shares memory
Yes. Every time you start a Java application, OS creates a dedicated process containing a JVM instance.
Heap, Metaspace, Code Cache.
Stack, PC register, Native stack.
- Exhausting heap (
-Xmx) - Exhausting metaspace (
-XX:MaxMetaspaceSize) - Running out of native memory
- Massive thread creation
- Memory leaks
Infinite recursion → stack memory exhausted per thread.
Because CPU must save & restore thread state, flush registers, update program counters, switch stack pointers.
Interpreter runs first → JIT optimizes repeated code paths → native code execution.
No. Heap is shared by all threads. Each thread only gets its own stack.
Each application runs in its own OS process with its own JVM instance and memory boundaries.
-
Java program execution always creates an OS-level process + JVM instance.
-
JVM memory consists of:
- Heap
- Metaspace
- Thread Stacks
- PC Registers
- Native Stacks
- Code Cache
-
Threads share heap but maintain individual stack and program counter.
-
JIT compilation boosts performance by converting bytecode → native code.
-
Memory limits are controlled by
-Xms,-Xmx,-XX:MaxMetaspaceSize. -
Context switching is expensive → use thread pools, not uncontrolled thread creation.
-
Understanding these internals helps optimize performance, debugging, and system design.