JVM and Memory
7 pages · ~5 min✓ Reviewed
Builds on Exceptions. Next up: Concurrency.
JDK, JRE and JVM Mental Model
1. Three layers
| Contains | Use | |
|---|---|---|
| JVM | loads, runs bytecode | execute |
| JRE | JVM + java.lang, java.util | run, no compiler |
| JDK | JRE + javac, jar, javadoc, jdb, jlink, jcmd | develop |
- Bytecode is platform-independent
- The JVM is platform-specific: write once, run anywhere
2. Containment
- JDK ⊃ JRE ⊃ JVM
- Since Java 11 Oracle ships no separate JRE
- Some vendors (Eclipse Temurin) still do
- Use a JDK or a
jlink-built runtime
3. Source to execution
- Hello.java
- javac
- Hello.classbytecode
- java launcherstarts JVM
- Class loader
- Execution engine
bash
javac Hello.java java Hello
compile with the JDK, run on the JVM
No compiler?
javac: command not found means you have only a JRE. Install a JDK.
Bytecode, JIT and Class Loading
1. Bytecode and the JIT
- Bytecode = stack-based, platform-neutral instructions in
.classfiles - Engine interprets first, then JIT-compiles hot methods to native code
- C1 = quick compile, C2 = optimizing; tiered uses both
bash
javac Hello.java javap -c Hello
javap -c disassembles to bytecode
- JIT tricks: inlining, escape analysis, dead-code elimination
- Warm-up cost makes short benchmarks lie: use JMH
2. Class Loading
- Bootstrapcore JDK classes
- Platformformerly Extension
- Applicationclasspath
- Each loader asks its parent first: user code cannot replace
java.lang.String - Loading is lazy: on first use
- Steps: load → link (verify, prepare, resolve) → initialize (static blocks run)
3. Missing Class Errors
| ClassNotFoundException | NoClassDefFoundError | |
|---|---|---|
| Kind | checked exception | Error |
| When | dynamic load fails, e.g. Class.forName | present at compile time, missing at runtime |
Remember
Delegation goes up to Bootstrap first; a JIT benchmark needs warm-up before timing.
Stack vs Heap Memory
1. Stack and Heap
| Stack | Heap | |
|---|---|---|
| Scope | one per thread | shared by all threads |
| Holds | frames | objects and arrays |
| Speed | fast, LIFO | slower |
| Freed | auto on return | only by GC |
- Frame = locals + operand stack + return address
- Method call pushes a frame; return pops it
2. What Lives Where
- Primitive local: the value sits in the stack frame
- Object local: reference on the stack, object on the heap
- Java is always pass-by-value: the reference is copied
java
int n = 5; // value on stack Point p = new Point(); // p on stack, object on heap void move(Point q) { q.x = 9; q = null; }
caller's p still points to the object, with x changed
3. Errors and Tuning
| Error | Cause | Tune |
|---|---|---|
StackOverflowError | too-deep or infinite recursion | -Xss |
OutOfMemoryError: Java heap space | heap exhausted | -Xms / -Xmx |
Not on the stack, not all in the heap
Class metadata lives in Metaspace (native memory, Java 8+). Static fields live on the heap, held by the Class object.
Garbage Collection Basics
1. What Is Garbage
- Eligible when no live GC root can reach it
- Roots: thread stacks, static fields, JNI refs
- No reference counting, so cycles are collected
2. Generations and Collectors
- Edennew objects
- S0 / S1survivors age
- Oldpromoted at threshold
- Most objects die young
- Minor GC: cleans Young, frequent, fast
- Major/Full GC: Old and more, slow, long stop-the-world pauses
| Collector | Pick it for |
|---|---|
| Serial | small apps |
| Parallel | throughput |
| G1 | default since Java 9, balanced low pause |
| ZGC / Shenandoah | very low pause, large heaps |
3. Avoid and Tune
System.gc()is only a hint, no guaranteefinalize()is deprecated: use try-with-resources orCleaner
bash
java -Xms512m -Xmx2g \
-XX:+UseG1GC -Xlog:gc Appheap size, collector, GC log
Remember
GC frees only unreachable objects. Reachable but unused objects are a leak.
String Pool and Immutability
1. Immutable and Pooled
- Immutable: value fixed after creation
concat,replace,toUpperCasereturn a new String- String pool: unique literals cached in the heap (Java 7+)
- Same literal text shares one object
| Code | Result |
|---|---|
"hi" | reuses or creates the pooled one |
new String("hi") | always a new heap object |
s.intern() | returns the pooled instance |
==compares references;equals()compares content
java
String b = new String("a"); System.out.println("a" == "a"); System.out.println(b == "a"); System.out.println(b.equals("a"));
output
true false true
2. Why Immutable, and Loops
- Safe pool sharing
- Thread safety
- Cached
hashCode: safe HashMap keys - Security: class names, paths, credentials
| Concat in loop | Thread-safe | Speed |
|---|---|---|
s += | n/a | many garbage objects |
StringBuilder | no | fast |
StringBuffer | synchronized | slower |
Mistake
Compare content with equals(), never ==.
Memory Leaks and Cheat Sheet
1. What a Leak Is
- Leak = objects still reachable but never used again
- GC cannot free them, so it ends in
OutOfMemoryError
| Cause | Fix |
|---|---|
| Static collection only grows | Bounded cache or WeakHashMap |
| Unclosed stream, connection | try-with-resources |
| Listener never deregistered | Remove listener on close |
| ThreadLocal in thread pool | remove() in finally |
| Bad equals/hashCode key | Override both consistently |
2. Diagnose
- Find the pid, check the heap, dump live objects
- Open
heap.hprofin Eclipse MAT or VisualVM - Add
-XX:+HeapDumpOnOutOfMemoryErrorto the JVM flags
bash
jps jcmd <pid> GC.heap_info jmap -dump:live,format=b,file=heap.hprof <pid>
3. Cheat Sheet
- JDK ⊃ JRE ⊃ JVM
javacthenjava;javap -cshows bytecode- Class loaders delegate to the parent first
- Stack per thread, heap shared
- Young → Old generations, G1 default
equals()for content, never==StringBuilderin loops- Heap dump on OOM
Pooled threads live on
A pooled thread never dies, so its ThreadLocal values stay. Always remove() in finally.
Check yourself
What do "hi" == new String("hi") and "hi".equals(new String("hi")) print?
false, thentruenew Stringalways makes a fresh heap object; the literal is the pooled one==compares references,equals()compares content
A service adds every request to a static List and never removes anything. It ends in OutOfMemoryError. Will a bigger -Xmx fix it?
- No, it only delays the crash
- The static field is a GC root, so the objects stay reachable and GC cannot free them
- Fix: bound the cache or remove entries
Class.forName("Missing") fails at runtime. Which is thrown, and is it checked?
- ClassNotFoundException, which is checked
- NoClassDefFoundError is the unchecked Error: the class was there at compile time but is missing at runtime