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?

StackHeap
Storesmethod frames: local variables, primitives, referencesall objects and arrays
Scopeone per threadshared by all threads
Lifetimefreed when the method returnsfreed by the garbage collector
Error when fullStackOverflowError (e.g. infinite recursion)OutOfMemoryError: Java heap space
Speedvery 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?

CollectorFlagBest for
Serial-XX:+UseSerialGCsmall heaps, single CPU
Parallel-XX:+UseParallelGCmaximum throughput (batch jobs)
G1 (default since Java 9)-XX:+UseG1GCbalanced latency and throughput, region-based
ZGC-XX:+UseZGCvery large heaps, sub-millisecond pauses (generational in Java 21+)
Shenandoah-XX:+UseShenandoahGClow 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.
  • ThreadLocal values 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?

  1. Declare the class final (no subclass can add mutability).
  2. Make all fields private final.
  3. No setters.
  4. 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; }