Advanced Interview Questions
Explain the JVM architecture.
.java ──javac──▶ .class (bytecode)
│
┌───────▼────────┐
│ Class Loader │ load → link (verify, prepare, resolve) → initialize
└───────┬────────┘
┌────────────────────▼──────────────────────┐
│ Runtime data areas │
│ Heap | Metaspace | Stacks | PC | Native │
└────────────────────┬──────────────────────┘
┌───────▼────────┐
│ Execution │ interpreter + JIT compiler + garbage collector
└────────────────┘Heap vs stack memory?
| Stack | Heap | |
|---|---|---|
| Stores | method frames: local variables, primitives, references | all objects and arrays |
| Scope | one per thread | shared by all threads |
| Lifetime | freed when the method returns | freed by the garbage collector |
| Error when full | StackOverflowError (e.g. infinite recursion) | OutOfMemoryError: Java heap space |
| Speed | very fast (LIFO) | slower allocation |
What is the difference between PermGen and Metaspace?
Up to Java 7, class metadata lived in PermGen, a fixed-size part of the heap, which often caused OutOfMemoryError: PermGen space. Java 8 replaced it with Metaspace in native memory, which grows automatically (cap it with -XX:MaxMetaspaceSize). The String pool moved to the regular heap in Java 7.
How does garbage collection work?
- GC frees objects that are no longer reachable from GC roots (stack locals, static fields, active threads, JNI refs).
- The heap is generational: new objects go to the Young generation (Eden + two Survivor spaces). Most die young and are cleaned by fast minor GCs.
- Objects that survive several cycles are promoted to the Old generation, which is collected less often (major/full GC).
- Basic phases: mark live objects, sweep dead ones, compact to avoid fragmentation.
Note: System.gc() is only a request; the JVM may ignore it.
Which garbage collectors are available?
| Collector | Flag | Best for |
|---|---|---|
| Serial | -XX:+UseSerialGC | small heaps, single CPU |
| Parallel | -XX:+UseParallelGC | maximum throughput (batch jobs) |
| G1 (default since Java 9) | -XX:+UseG1GC | balanced latency and throughput, region-based |
| ZGC | -XX:+UseZGC | very large heaps, sub-millisecond pauses (generational in Java 21+) |
| Shenandoah | -XX:+UseShenandoahGC | low pause, concurrent compaction |
Can Java have memory leaks? Give examples.
Yes: GC only removes unreachable objects, so anything still referenced stays forever. Common causes:
- Static collections (caches) that only grow.
- Listeners/callbacks registered but never removed.
ThreadLocalvalues not removed in thread pools.- Unclosed resources (streams, connections).
- Keys with mutable fields or missing
hashCode()in a HashMap.
Diagnose with a heap dump (-XX:+HeapDumpOnOutOfMemoryError, jmap) and analyse in Eclipse MAT or VisualVM.
How does class loading work? What is parent delegation?
Class loaders form a hierarchy: Bootstrap (core java.*) → Platform (other JDK modules) → Application (your classpath). With parent delegation, a loader first asks its parent; only if the parent cannot find the class does it load it itself. This prevents your code from replacing core classes like java.lang.String and avoids loading a class twice.
What is the JIT compiler?
The JVM starts by interpreting bytecode. It profiles which methods and loops are "hot" and the Just-In-Time compiler (C1 quickly, then C2 with heavy optimisation) compiles them to native code. Optimisations include inlining, escape analysis (allocating objects on the stack), loop unrolling and dead-code removal. This is why Java apps get faster after a warm-up period.
How do you make a class immutable?
- Declare the class
final(no subclass can add mutability). - Make all fields
private final. - No setters.
- Defensively copy mutable inputs in the constructor and mutable outputs in getters (e.g.
List.copyOf).
Tip: A record gives you final fields and no setters for free, but you still need List.copyOf for list components.
How do you write a thread-safe Singleton?
// 1. Bill Pugh holder (lazy, thread-safe, no locking cost)
public class Config {
private Config() {}
private static class Holder { static final Config INSTANCE = new Config(); }
public static Config getInstance() { return Holder.INSTANCE; }
}
// 2. Double-checked locking (field MUST be volatile)
public class Db {
private static volatile Db instance;
private Db() {}
public static Db getInstance() {
if (instance == null) {
synchronized (Db.class) {
if (instance == null) instance = new Db();
}
}
return instance;
}
}
// 3. Enum singleton: also safe against reflection and serialization
public enum Registry { INSTANCE; }