JVM memory model, garbage collectors and tuning, diagnostics tooling, memory leaks, class loading, JIT, references, generics, JPMS, native interop, low-level performance, OOM scenarios, containers and JMH.
Theory
Q1
What are the main runtime memory areas of the JVM?
basic
The JVM splits memory into the heap (objects, shared), per-thread stacks, Metaspace (class metadata, native memory), the code cache (JIT output) and other native areas (direct buffers, thread structures, GC data).
Heap: young generation (Eden + two survivors) and old generation in generational collectors.
Each thread has a stack of frames holding locals, operand stack and return info; -Xss sets its size.
Metaspace replaced PermGen in Java 8 and lives in native memory.
⚠ Follow-up traps
Is -Xmx the total process memory? No. Metaspace, stacks, code cache, direct buffers and GC structures come on top of it.
Are strings in PermGen? Not since Java 7; the string pool lives in the heap.
#jvm#memory#heap
Q2
What is stored on the stack versus the heap?
basic
Local primitives and object references live in stack frames; all objects and arrays are allocated on the heap (conceptually). A frame is discarded when the method returns.
Objects can be shared across threads; stack data is thread-confined.
Escape analysis may scalar-replace an object so it never exists on the heap.
⚠ Follow-up traps
Is a local int[] on the stack? The reference is, the array is on the heap (unless the JIT eliminates the allocation).
What causes StackOverflowError? Frames exhausting the thread stack, typically unbounded recursion.
#stack#heap#references
Q3
What does the Java Memory Model (JMM) guarantee?
intermediate
The JMM defines when a write by one thread is guaranteed visible to a read by another through the happens-before relation. Without happens-before, the compiler and CPU may reorder or cache writes.
Edges: unlock -> later lock of the same monitor; volatile write -> later read of that variable; Thread.start(); Thread.join(); final-field freeze at constructor end.
Program order within one thread also forms happens-before.
⚠ Follow-up traps
Does synchronized only give mutual exclusion? No, it also gives visibility and ordering.
Is a data race always a crash? No, it yields undefined-ish but bounded results such as stale or torn (for non-volatile long/double on old 32-bit VMs) values.
#jmm#happens-before#visibility
Q4
What does `volatile` do and not do?
basic
volatile guarantees visibility and prevents reordering around the access (acquire/release semantics plus sequential consistency among volatiles). It does not make compound operations atomic.
volatile int counter;void inc() { counter++; } // read, add, write: lost updates possible
Use AtomicInteger, LongAdder or locks for read-modify-write.
Good for flags and safe publication of immutable objects.
⚠ Follow-up traps
Is volatile long write atomic? Yes; the spec guarantees atomicity for volatile long and double.
Does volatile on a reference make the object's fields volatile? No, only the reference variable itself.
#volatile#visibility#atomicity
Q5
Explain safe publication and `final` field semantics.
intermediate
An object is safely published if its reference is handed over through a happens-before edge (volatile, lock, static initializer, concurrent collection). Properly constructed objects with final fields are visible fully initialized to any thread, even via a data race.
The this reference must not escape from the constructor.
Non-final fields of an unsafely published object may appear with default values.
⚠ Follow-up traps
Does final on a reference make the referenced object immutable? No, only the reference is frozen; the freeze guarantee covers what was reachable through final fields at construction end.
Is double-checked locking safe? Only if the field is volatile (Java 5+).
#jmm#final#safe-publication
Q6
How does generational garbage collection work?
basic
Most objects die young (weak generational hypothesis), so the heap is split into a young generation collected often and cheaply by copying survivors, and an old generation collected rarely.
New objects go to Eden; a minor GC copies live objects to a survivor space, ageing them.
After surviving MaxTenuringThreshold cycles (or if survivors overflow) objects are promoted to old.
Card tables / remembered sets track old-to-young references so a young GC need not scan old.
⚠ Follow-up traps
Does a minor GC stop the world? Yes, in all HotSpot collectors the young collection is STW (G1, Parallel) or partly concurrent in generational ZGC/Shenandoah variants.
What is a promotion failure? Old space cannot accept promoted objects, forcing a full GC.
#gc#generational#young-gen
Q7
How does reachability determination work (GC roots)?
basic
The GC marks everything reachable from GC roots: live thread stacks and registers, static fields of loaded classes, JNI references, and monitors held. Unreached objects are garbage, regardless of cycles.
Java does not use reference counting, so cyclic structures are collected.
Class loaders and their classes are roots-reachable only through live references.
⚠ Follow-up traps
Can an object with a finalizer be resurrected? Historically yes; finalization is deprecated for removal (Java 18+), use Cleaner.
Is a local variable reachable after its last use? The JIT may treat it as dead; use Reference.reachabilityFence when needed.
#gc#roots#reachability
Q8
Compare Serial, Parallel, G1, ZGC and Shenandoah at a high level.
intermediate
Serial and Parallel are STW, throughput-oriented; G1 is the balanced region-based default; ZGC and Shenandoah are concurrent low-pause collectors.
Serial: single thread, tiny heaps and containers.
Parallel: multi-threaded STW, best raw throughput, pauses grow with heap.
G1: regions, mixed collections, pause target via -XX:MaxGCPauseMillis (default 200ms).
Which is the default? G1 on server-class machines (2+ CPUs, 1792MB+) since Java 9; otherwise Serial.
Does concurrent mean no pauses? Pauses still exist but are short and mostly size-independent.
#gc#collectors#comparison
Q9
How does G1 organize the heap and collect it?
intermediate
G1 divides the heap into equal regions (1-32MB) that are dynamically Eden, Survivor, Old or Humongous. It collects a "collection set" chosen to meet the pause target, preferring regions with most garbage.
Young GC: STW evacuation of Eden/Survivor regions.
Concurrent marking starts when occupancy hits InitiatingHeapOccupancyPercent (45%; adaptive since Java 9).
Mixed GCs then evacuate young plus the most garbage-rich old regions.
Full GC (parallel since Java 10) is the fallback.
⚠ Follow-up traps
Does G1 guarantee the pause target? No, it is a soft goal.
What are humongous objects? Objects at least half a region; they are allocated in contiguous old regions and are expensive.
#g1#regions#mixed-gc
Q10
What is a humongous allocation in G1 and why is it a problem?
advanced
An object of size >= region/2 is humongous: it takes whole contiguous regions directly in old space, bypassing Eden. They can cause fragmentation, premature concurrent cycles and Full GCs.
Reclaimed at young GC if proven dead (eager reclaim) for primitive arrays without references.
Does a 600KB array with 1MB regions count? Yes, it exceeds 512KB.
Does increasing heap fix it? Not necessarily; contiguity is the problem, not total space.
#g1#humongous#fragmentation
Q11
How does ZGC achieve low pauses?
advanced
ZGC does marking, relocation and reference processing concurrently with application threads. Load barriers check pointer metadata ("colors") and fix up stale references on access, so STW phases only scan roots.
Pauses are typically well under 1ms and do not scale with heap or live set.
Supports multi-terabyte heaps; generational ZGC (-XX:+ZGenerational, Java 21; the only mode from Java 24) improves throughput.
Costs: load barrier overhead, more CPU and memory headroom required.
⚠ Follow-up traps
Can ZGC still throw OOM or stall allocation? Yes, if allocation outpaces reclamation it stalls threads ("Allocation Stall").
Is ZGC the answer for throughput batch jobs? Usually Parallel wins there.
#zgc#load-barrier#colored-pointers
Q12
How does Shenandoah differ from ZGC?
advanced
Both are concurrent-compacting, but Shenandoah uses a load-reference barrier (since Java 13, formerly Brooks forwarding pointer) with reference-forwarding in the object, while ZGC uses colored pointers and load barriers on 64-bit only.
Shenandoah ships in OpenJDK builds from many vendors (not Oracle JDK); ZGC is in all.
Shenandoah works on 32-bit and compressed oops; ZGC historically did not support compressed oops (it does not use them).
Enable with -XX:+UseShenandoahGC.
⚠ Follow-up traps
Does ZGC use compressed oops? No, so its pointer overhead is larger on heaps under 32GB.
Does pause time depend on heap size for either? Not meaningfully; it depends on roots.
#shenandoah#zgc#barriers
Q13
What does the Parallel collector optimize and when do you choose it?
intermediate
Parallel GC (-XX:+UseParallelGC) uses multiple threads for STW young and full collections to maximize application throughput, accepting longer pauses.
Good for batch/ETL jobs where total runtime matters and pauses do not.
Does jmap -dump:live pause the JVM? Yes, it forces a full GC and STW.
Can you attach as a different user? No; the tool must run as the same OS user as the target JVM.
#tooling#jcmd#jstack
Q17
What is Java Flight Recorder (JFR) and how do you use it?
intermediate
JFR is a low-overhead (typically 1-2%) event recorder built into the JVM, free since Java 11. It captures GC, allocation, locks, I/O, CPU samples and custom events into a .jfr file analyzed in JDK Mission Control or jfr CLI.
default profile is safe for always-on; profile has more detail.
Allocation sampling attributes hot allocation sites without full instrumentation.
⚠ Follow-up traps
Does JFR suffer safepoint bias? Much less than many profilers, since it samples asynchronously (though not fully free of it).
Can you add application events? Yes, extend jdk.jfr.Event.
#jfr#profiling#observability
Q18
How does a class loader hierarchy work and what is parent delegation?
intermediate
Loaders form a hierarchy: bootstrap (core modules, native), platform (formerly extension), application (classpath). loadClass first delegates to the parent and only loads itself if the parent fails.
Guarantees core classes like java.lang.String cannot be replaced by user code.
A class's identity is (name, defining loader); the same bytes in two loaders are different classes.
Servlet containers and OSGi invert or break delegation for isolation.
⚠ Follow-up traps
Why a ClassCastException between identical class names? They were defined by different loaders.
Does getClass().getClassLoader() return null for String? Yes, bootstrap is represented as null.
#classloader#delegation
Q19
Describe the class loading and initialization lifecycle.
intermediate
Loading -> linking (verification, preparation, resolution) -> initialization. Initialization runs static initializers (<clinit>) exactly once, lazily, under a lock, on first active use.
Triggers: new, static method/non-constant field access, reflection Class.forName(name), subclass init.
Constants (static final compile-time) do not trigger init.
Circular initialization between classes can see default values or deadlock across threads.
⚠ Follow-up traps
Does loading imply initialization? No; Class.forName(n, false, loader) loads only.
What if <clinit> throws? The class becomes unusable, with NoClassDefFoundError on later use.
#classloading#initialization#clinit
Q20
What are Metaspace and PermGen, and how do they differ?
basic
PermGen was a fixed-size heap region for class metadata (removed in Java 8). Metaspace stores that metadata in native memory and grows on demand, bounded by -XX:MaxMetaspaceSize (unlimited by default).
Metaspace is freed when the owning class loader is garbage collected.
-XX:MetaspaceSize is the high-water mark that triggers the first GC, not a reservation.
⚠ Follow-up traps
Can Metaspace still run out? Yes, via class loader leaks or dynamic class generation, reported as OutOfMemoryError: Metaspace.
Does Java 8 -XX:PermSize still work? It is ignored with a warning (and removed later).
#metaspace#permgen
Q21
How does the JIT and tiered compilation work?
intermediate
HotSpot interprets bytecode first, profiles it, then compiles hot methods: C1 (fast, light optimization) at tiers 1-3 and C2 (aggressive) at tier 4. Tiered compilation is on by default.
Tier 0 interpreter; tier 3 is C1 with full profiling; tier 4 is C2.
Thresholds are invocation plus back-edge (loop) counters; OSR compiles hot loops mid-execution.
Deoptimization reverts to the interpreter when speculative assumptions break (class loaded, type profile changes).
⚠ Follow-up traps
Why is the first request slow? Cold code runs interpreted or in C1; warm-up is required.
What is OSR? On-stack replacement: switching a running loop to compiled code.
#jit#tiered#c1-c2
Q22
Name key JIT optimizations.
intermediate
Inlining is the foundational optimization enabling the rest: escape analysis, constant folding, dead code elimination, loop unrolling, range-check elimination, devirtualization and lock coarsening/elision.
Monomorphic and bimorphic call sites are inlined with a type guard; megamorphic sites use vtable/itable dispatch.
Methods under MaxInlineSize (35 bytecodes) or hot ones up to FreqInlineSize (325) are inlinable.
Inspect with -XX:+PrintCompilation, -XX:+UnlockDiagnosticVMOptions -XX:+PrintInlining.
⚠ Follow-up traps
Why can a large method be slow? It may exceed inline or huge-method limits (8000 bytecodes is not compiled at all).
Does final help inlining? Little; the JIT uses class hierarchy analysis.
#jit#inlining#optimizations
Q23
What is escape analysis and scalar replacement?
advanced
C2 analyzes whether an object can be observed outside its method or thread. If not, it can scalar-replace it (fields become locals, no allocation) and elide its locks.
double dist(double x, double y) { Point p = new Point(x, y); // may never be allocated return Math.sqrt(p.x * p.x + p.y * p.y);}
Fails when the object is stored in a field, passed to a non-inlined call, or returned.
Flag: -XX:+DoEscapeAnalysis (default on).
Not "stack allocation": HotSpot has no real stack allocation.
⚠ Follow-up traps
Does escape analysis work in the interpreter or C1? No, only C2 (and Graal).
Is it guaranteed? No; it is heuristic and depends on inlining success.
#escape-analysis#jit#allocation
Q24
Compare strong, soft, weak and phantom references.
intermediate
Strong references block collection. Soft are cleared under memory pressure, weak at the next GC when no strong refs remain, and phantom are enqueued after the object is finalized and used for cleanup actions (get() always returns null).
Soft: memory-sensitive caches (policy via -XX:SoftRefLRUPolicyMSPerMB).
Weak: canonicalizing maps like WeakHashMap, listener registries.
Is a soft-reference cache a good cache? Poor: GC timing is unpredictable; prefer Caffeine with size bounds.
Does WeakHashMap weakly hold values? No, only keys; a value referencing its key prevents removal.
#references#soft#weak#phantom
Q25
What is the difference between `Cleaner`, `finalize` and try-with-resources?
intermediate
Try-with-resources gives deterministic closing. Cleaner (Java 9+) is a safety net run by a daemon thread when an object becomes phantom reachable. finalize() is deprecated for removal because it is slow, unpredictable and can resurrect objects.
The cleaning action must not reference the object it cleans, or it will never run.
Never rely on either for releasing scarce resources promptly.
⚠ Follow-up traps
Is finalize guaranteed to run? No, not even at JVM exit.
Does a finalizable object cost extra? Yes, it needs at least two GC cycles to reclaim and a finalizer queue.
#cleaner#finalize#resources
Q26
What is type erasure and what are its consequences?
basic
Generic type parameters are checked at compile time and erased to their bounds (or Object) in bytecode, with casts inserted. At runtime List<String> and List<Integer> are both List.
Cannot do new T(), new T[n], instanceof List<String>, or overload by type argument.
Generic info persists in signatures and can be read through reflection (getGenericSuperclass).
Bridge methods preserve polymorphism after erasure.
⚠ Follow-up traps
Does List<String> equal List<Integer> class?getClass() is identical.
Can you catch T extends Exception? No; generic classes cannot extend Throwable, and you cannot catch a type variable.
#generics#erasure
Q27
Explain wildcards and the PECS rule.
intermediate
Producer Extends, Consumer Super: use ? extends T when you only read T from a structure and ? super T when you only write T into it. List<? extends Number> is covariant and read-only (except null); List<? super Integer> is contravariant and write-friendly.
static <T> void copy(List<? super T> dst, List<? extends T> src) { for (T t : src) dst.add(t);}
Collections.copy and Comparator<? super T> follow PECS.
? alone means unknown type, usable for read as Object.
⚠ Follow-up traps
Can you add an Integer to List<? extends Number>? No, the actual element type is unknown; only null compiles.
Is List<Integer> a subtype of List<Number>? No, generics are invariant.
#generics#wildcards#pecs
Q28
What is heap pollution and why do varargs of generics warn?
advanced
Heap pollution occurs when a variable of a parameterized type refers to an object that is not of that type, which surfaces later as a ClassCastException at an unrelated line. Generic varargs create an array of the erased type, which enables it.
static <T> T[] toArray(T... xs) { return xs; } // unchecked warningString[] s = toArray("a", 1); // CCE at call site
@SafeVarargs asserts the method does not expose the array (allowed on static, final, private methods).
Raw types and unchecked casts are the other sources.
⚠ Follow-up traps
Why does new List<String>[10] not compile? Arrays are reified and covariant; generic arrays would break type safety.
Where does the CCE fire? At the compiler-inserted cast, far from the pollution.
#generics#heap-pollution#varargs
Q29
What are bridge methods and why do they exist?
advanced
When a subclass overrides a generic method with a concrete type, the compiler generates a synthetic bridge method with the erased signature that casts and delegates, preserving polymorphism.
Can bridges cause surprising stack traces? Yes, an extra synthetic frame appears.
Can you call set("x") on a raw IntBox? It compiles with a warning and throws ClassCastException in the bridge.
#generics#bridge-methods#erasure
Q30
How do bounded type parameters and recursive bounds work?
intermediate
<T extends Comparable<T>> constrains T to types comparable with themselves. Multiple bounds use &: <T extends Number & Comparable<T>>, with the class first.
static <T extends Comparable<? super T>> T max(Collection<? extends T> c) { Iterator<? extends T> it = c.iterator(); T best = it.next(); while (it.hasNext()) { T x = it.next(); if (x.compareTo(best) > 0) best = x; } return best;}
? super T in Comparable lets a java.sql.Timestamp use its parent's compareTo.
Erasure of the type variable is its first bound.
⚠ Follow-up traps
Why Comparable<? super T> rather than Comparable<T>? Subclasses of a comparable class would otherwise be rejected.
Can a bound be a type variable? Yes (<T, U extends T>), but not a lower bound on a type parameter.
#generics#bounds#comparable
Q31
What is a module (JPMS) and what problems does it solve?
intermediate
Java 9's module system adds module-info.java declaring a module's name, dependencies (requires) and exported packages. It provides strong encapsulation and reliable configuration at startup.
module com.acme.orders { requires java.sql; requires transitive com.acme.common; exports com.acme.orders.api; opens com.acme.orders.model to com.fasterxml.jackson.databind; uses com.acme.spi.Tax; provides com.acme.spi.Tax with com.acme.orders.VatTax;}
Non-exported packages are inaccessible even via public and reflection.
jlink builds trimmed runtime images.
⚠ Follow-up traps
Does exports allow reflection on private members? No, opens is needed for deep reflection.
Is a modular app required on Java 9+? No, classpath code lives in the unnamed module.
#jpms#modules#encapsulation
Q32
What are automatic modules, the unnamed module and `--add-opens`?
advanced
A plain JAR on the module path becomes an automatic module (name from Automatic-Module-Name or filename) that exports and opens everything and reads all modules. The classpath contents form the unnamed module.
Since Java 16/17, JDK internals are strongly encapsulated: --illegal-access is gone.
Workarounds for legacy libraries: --add-opens java.base/java.lang=ALL-UNNAMED, --add-exports.
Split packages across modules are forbidden on the module path.
⚠ Follow-up traps
Why does a library fail on Java 17 with InaccessibleObjectException? It reflects into JDK internals; add --add-opens or upgrade it.
Can named modules read the unnamed module? No.
#jpms#automatic-module#add-opens
Q33
What is the Java Native Interface and what are its drawbacks?
intermediate
JNI lets Java call C/C++ through native methods bound to functions in a shared library loaded with System.loadLibrary. Headers are generated by javac -h.
Drawbacks: boilerplate, crashes the whole JVM on bugs, manual local/global reference management, slow-ish transitions, hard to test and port.
Critical sections pin arrays or hamper GC (GetPrimitiveArrayCritical).
Used by many JDK internals and libraries (zstd, RocksDB).
⚠ Follow-up traps
Can a segfault in native code be caught in Java? No; the JVM process dies (hs_err log).
Do JNI local references leak? Yes in long native loops unless deleted or frames are used.
#jni#native#interop
Q34
What is the Foreign Function and Memory API (Project Panama)?
advanced
The FFM API (java.lang.foreign, final in Java 22) replaces JNI and Unsafe for native interop: Linker binds C functions, MemorySegment gives bounds-checked off-heap access, and Arena controls lifetime deterministically.
Linker linker = Linker.nativeLinker();MethodHandle strlen = linker.downcallHandle( linker.defaultLookup().find("strlen").orElseThrow(), FunctionDescriptor.of(ValueLayout.JAVA_LONG, ValueLayout.ADDRESS));try (Arena a = Arena.ofConfined()) { long n = (long) strlen.invokeExact(a.allocateFrom("hello")); // 5 (Java 22+)}
Pure Java, no C glue; jextract generates bindings from headers.
Safe: spatial and temporal checks prevent use-after-free.
⚠ Follow-up traps
Is the API restricted? Yes, native access warns unless --enable-native-access is given.
Was it final in Java 21? No, it was a preview there and the string allocation method was named allocateUtf8String; Java 22 renamed it to allocateFrom.
#panama#ffm#native
Q35
What are direct buffers and when should you use them?
intermediate
ByteBuffer.allocateDirect allocates memory outside the heap, avoiding a copy during native I/O. Allocation is expensive and freed only when the small Buffer object is garbage collected.
Limited by -XX:MaxDirectMemorySize (defaults to roughly -Xmx).
Good for long-lived, reused buffers (Netty pools them).
Leaks appear as native memory growth with a small heap; error: OutOfMemoryError: Direct buffer memory.
⚠ Follow-up traps
Does -Xmx limit direct memory? Not directly; separate cap.
Are heap buffers copied for I/O? Yes, the JDK copies through a temporary direct buffer.
#nio#direct-buffer#off-heap
Q36
What is false sharing?
advanced
Two threads write independent variables that sit on the same 64-byte cache line, so the line ping-pongs between cores via the coherence protocol and throughput collapses.
Symptom: scaling worse than linear with threads while no lock contention exists.
Verify with perf c2c or JMH multi-threaded benchmarks.
⚠ Follow-up traps
Does volatile cause false sharing? Any write does; volatile just makes it worse by forcing visibility.
Does the JVM reorder fields to avoid it? It may reorder, but not to prevent sharing.
#false-sharing#cpu-cache#performance
Q37
What is the cost of boxing and how do you avoid it?
basic
Autoboxing wraps primitives into objects (Integer), costing allocation, an extra pointer indirection and GC pressure. List<Integer> uses about 16 bytes per element plus the 4-8 byte reference versus 4 for int[].
Integer.valueOf caches -128..127 (extendable via -XX:AutoBoxCacheMax).
Escape analysis can remove some boxing, not reliably.
⚠ Follow-up traps
Is new Integer(5) == new Integer(5)? False; also deprecated for removal.
What does Integer null in arithmetic do?NullPointerException on unboxing.
#boxing#performance#allocation
Q38
How does object allocation work in HotSpot (TLAB)?
advanced
Each thread gets a Thread-Local Allocation Buffer, a chunk of Eden, so most allocations are a pointer bump with no locking. When the TLAB is full, a new one is requested; huge objects bypass it.
Allocation is typically ~10-20 instructions in compiled code.
Is new slow in Java? No, usually cheaper than malloc; the cost is in GC and cache effects.
Are objects zeroed? Yes, memory is zeroed before use (or the JIT proves it is overwritten).
#allocation#tlab
Q39
What are compressed oops and the 32GB heap boundary?
advanced
With heaps under ~32GB, HotSpot stores references as 32-bit values scaled by object alignment (8 bytes), halving reference size. Beyond about 32GB the JVM switches to 64-bit references and memory use jumps.
-XX:+UseCompressedOops is automatic; check with -XX:+PrintFlagsFinal.
A 40GB heap may hold less data than a 31GB one.
Class pointers are compressed separately (UseCompressedClassPointers).
⚠ Follow-up traps
Does the limit change with -XX:ObjectAlignmentInBytes=16? Yes, up to ~64GB at the cost of padding.
Does ZGC use compressed oops? No.
#compressed-oops#heap-sizing
Q40
What are `Unsafe` and `VarHandle`?
advanced
sun.misc.Unsafe offers unchecked memory and CAS operations used by JDK and frameworks; it is being phased out. VarHandle (Java 9) is the supported replacement for fields, arrays and buffers with defined memory-ordering modes.
class Counter { volatile int n; static final VarHandle N; static { try { N = MethodHandles.lookup().findVarHandle(Counter.class, "n", int.class); } catch (ReflectiveOperationException e) { throw new ExceptionInInitializerError(e); } } int incr() { return (int) N.getAndAdd(this, 1) + 1; }}
JEP 471 deprecates memory-access methods of Unsafe for removal.
⚠ Follow-up traps
Why store a VarHandle in static final? So the JIT treats it as a constant and inlines the access.
Is Unsafe accessible via getUnsafe()? Only for the bootstrap loader; frameworks reflect theUnsafe.
#unsafe#varhandle#atomics
Q41
How do Java memory ordering modes (plain/opaque/acquire-release/volatile) differ?
advanced
From weakest to strongest: plain (no guarantees), opaque (coherence, no tearing, eventually visible), release/acquire (writes before a release are visible after the matching acquire), volatile (total order across all volatile accesses).
Release/acquire suffices for producer/consumer handoff and is cheaper on ARM.
x86 gives acquire/release nearly for free; volatile writes still need a fence.
Used inside AtomicXxx.lazySet (release) and JCTools queues.
⚠ Follow-up traps
Is acquire/release sequentially consistent? No, independent writes can be observed in different orders by different threads.
Does opaque order other variables? No, only the single variable.
#varhandle#memory-ordering#jmm
Q42
How does the JVM detect container limits (ergonomics)?
intermediate
Since Java 10 (backported to 8u191) the JVM reads cgroup limits to size the default heap and CPU count. Default max heap is 25% of container memory; -XX:MaxRAMPercentage adjusts it.
availableProcessors() derives from CPU quota; this sizes GC threads, ForkJoin common pool and compiler threads.
-XX:ActiveProcessorCount=N overrides.
-XX:+UseContainerSupport is on by default.
⚠ Follow-up traps
Why 25% by default? It is conservative for non-heap usage; set 60-75% for dedicated JVM containers.
Does a 1 CPU limit choose G1? No, it picks Serial (needs 2+ CPUs and 1792MB).
#containers#ergonomics#cgroups
Q43
How should you size container memory for a JVM?
intermediate
Container limit = heap + Metaspace + code cache + thread stacks + direct memory + GC overhead + native libraries. A rough rule is heap at 60-75% of the limit.
FROM eclipse-temurin:21-jreENV JAVA_TOOL_OPTIONS="-XX:MaxRAMPercentage=70 -XX:+ExitOnOutOfMemoryError"COPY app.jar /app.jarENTRYPOINT ["java", "-jar", "/app.jar"]
Verify real footprint with -XX:NativeMemoryTracking=summary and jcmd VM.native_memory.
The kernel OOM killer sends SIGKILL (exit 137) when the cgroup limit is exceeded, with no Java stack trace.
⚠ Follow-up traps
Is exit code 137 a Java OOM? No, it is the kernel killing the process.
Do requests or limits drive ergonomics? Limits.
#containers#memory-sizing#kubernetes
Q44
What is Native Memory Tracking (NMT)?
intermediate
NMT is a JVM feature tracking its own native allocations by category (heap, class, thread, code, GC, internal, symbol). Enable with -XX:NativeMemoryTracking=summary|detail, query with jcmd <pid> VM.native_memory summary.
Use baseline and summary.diff to find growth.
Overhead is 5-10% for detail, small for summary.
It does not see allocations by third-party native libraries or glibc fragmentation.
⚠ Follow-up traps
RSS keeps growing yet NMT is flat? Suspect glibc malloc arenas (MALLOC_ARENA_MAX=2) or native libs.
Does NMT track direct buffers? Yes, under "Other"/"Internal".
#nmt#native-memory#diagnostics
Q45
What is a safepoint and why does it matter?
advanced
A safepoint is a state in which all Java threads are paused at known points so the VM can perform operations such as GC, deoptimization, biased lock revocation (removed) or thread dumps. Threads poll at loop back-edges and method returns.
"Time to safepoint" can be long if one thread is in a long counted loop without polls (reduced by loop strip mining).
Diagnose with -Xlog:safepoint.
Pauses reported as application-stopped time can exceed GC time.
⚠ Follow-up traps
Are threads in native code a problem? No, they are considered safe, but must block on return.
Does a thread dump require a safepoint? Yes for jstack, which is why dumps can be delayed.
#safepoint#stw#latency
Q46
Why is a naive Java microbenchmark unreliable, and what does JMH do?
intermediate
Warm-up, JIT, dead-code elimination, constant folding, loop optimizations and GC make hand-rolled timing loops lie. JMH runs forked JVMs, warm-ups, multiple iterations and provides Blackhole to defeat dead-code elimination.
@State(Scope.Thread)@BenchmarkMode(Mode.AverageTime) @OutputTimeUnit(TimeUnit.NANOSECONDS)@Warmup(iterations = 5) @Measurement(iterations = 5) @Fork(2)public class ParseBench { String s = "12345"; @Benchmark public int parse() { return Integer.parseInt(s); }}
Return results or sink to Blackhole; keep inputs in non-final @State fields.
Use -prof gc and -prof perfasm for allocation and assembly.
⚠ Follow-up traps
Why not System.nanoTime in a loop? The JIT may remove or hoist the work.
Why @Fork? Profile pollution from earlier code in the same JVM skews results.
#jmh#benchmarking
Q47
What are JMH pitfalls (constant folding, state, threads)?
advanced
Pitfalls: final or literal inputs get constant-folded; shared mutable Scope.Benchmark state gives false sharing; benchmark loops written by hand defeat JMH's harness; trivial benchmarks measure the harness.
Never write loops inside @Benchmark expecting JMH to scale; JMH may unroll them incorrectly.
@Setup(Level.Invocation) adds overhead comparable to the work measured.
Check error bars; compare distributions, not single numbers; pin CPU frequency.
⚠ Follow-up traps
Does a void benchmark that computes and discards result run? The JIT may delete it; consume with Blackhole.consume.
Is a 1-ns difference real? Usually noise.
#jmh#pitfalls#benchmarking
Q48
Describe common causes of memory leaks in Java.
basic
A Java leak is an object that is reachable but no longer needed. The usual causes are unbounded collections, static fields, listeners never deregistered, ThreadLocal in pooled threads, unclosed resources and class loader leaks.
Caches without eviction or size bound.
Inner classes holding this; lambdas capturing large objects.
ThreadLocal values staying alive for as long as pool threads live.
String.substring-style retention was fixed in Java 7u6.
⚠ Follow-up traps
Can Java leak without a bug in your code? Yes, in libraries, drivers and frameworks (e.g., JDBC driver registration across redeploys).
Does GC prevent all leaks? No, only unreachable memory is freed.
#memory-leak#heap
Q49
How does `ThreadLocal` leak and how do you prevent it?
intermediate
Each thread holds a ThreadLocalMap with weak keys and strong values. In a pooled thread that never dies, a value is retained until the thread removes it, and if the value references its class loader, the whole application is retained after redeploy.
Always remove() in finally for request-scoped data.
Prefer scoped values (preview/final in recent JDKs) or explicit passing with virtual threads.
⚠ Follow-up traps
Are the keys leaked? No, weak, so stale entries have null keys but live values until cleaned lazily.
Do virtual threads make ThreadLocal fine? They support it, but millions of threads times a heavy value is costly.
#threadlocal#memory-leak#thread-pool
Q50
What are `WeakHashMap` and `IdentityHashMap` for?
intermediate
WeakHashMap holds keys weakly, so an entry disappears when the key is no longer strongly referenced elsewhere; useful for metadata attached to objects. IdentityHashMap compares keys with == and System.identityHashCode, used for graph traversal and serialization.
WeakHashMap is not thread-safe; wrap with Collections.synchronizedMap.
Interned String literals or boxed small values as keys never expire.
⚠ Follow-up traps
Why would a WeakHashMap never shrink? The value strongly references the key.
Does IdentityHashMap honor the Map contract? No, it intentionally violates equals-based semantics.
#weakhashmap#identityhashmap#references
Q51
What is Class Data Sharing (CDS) and AOT/GraalVM native image at a high level?
advanced
CDS maps a pre-parsed archive of JDK (and with AppCDS, application) classes into memory, cutting startup and sharing memory across JVMs. GraalVM Native Image compiles to a native binary ahead of time: instant startup and low memory, but no JIT peak performance, closed-world assumptions and reflection configuration.
Dynamic archive: -XX:ArchiveClassesAtExit=app.jsa then -XX:SharedArchiveFile=app.jsa.
Native image needs metadata for reflection, proxies and resources (Spring Boot 3 generates hints).
Project Leyden is working toward AOT caches for the standard JVM.
⚠ Follow-up traps
Is native image always faster? Faster startup, often lower peak throughput than C2.
Is CDS enabled by default? The JDK classes archive is since Java 12.
#cds#graalvm#startup
Q52
How do virtual threads (Java 21) relate to JVM memory and diagnostics?
advanced
Virtual threads are heap-allocated continuations mounted on carrier threads; their stacks are stored as stack chunk objects on the heap and grow on demand, so millions are feasible.
Pinning (blocking in synchronized before Java 24, or native frames) stalls the carrier; diagnose with -Djdk.tracePinnedThreads=full or the JFR jdk.VirtualThreadPinned event.
jstack does not list them; use jcmd <pid> Thread.dump_to_file -format=json file.
They are not GC roots until mounted/referenced; abandoned blocked ones can leak.
⚠ Follow-up traps
Do virtual threads speed up CPU-bound code? No, only waiting-bound workloads.
Should you pool virtual threads? No, create one per task.
#virtual-threads#loom#diagnostics
Scenarios
Q53
A service throws `OutOfMemoryError: Java heap space` after hours of traffic. How do you investigate?
intermediate
Capture a heap dump at failure, then find the dominator tree to see what retains the memory. Distinguish a leak (steady growth after full GCs) from undersizing (stable but too small).
Open in Eclipse MAT: leak suspects report, dominator tree, path to GC roots.
Compare two histograms taken minutes apart to see which class count grows.
Check GC log: if post-GC occupancy keeps rising, it is a leak.
⚠ Follow-up traps
Is a bigger -Xmx a fix? Only for undersizing; a leak just takes longer to fail.
Why might no dump appear? Disk full, wrong path, or container killed (SIGKILL) before dumping.
#oom#heap-dump#diagnostics
Q54
`OutOfMemoryError: GC overhead limit exceeded` appears. What does it mean?
intermediate
The Parallel collector spent more than 98% of time in GC while recovering less than 2% of the heap, over several consecutive collections. It is a heuristic that fails fast instead of crawling.
The root cause is the same as heap exhaustion: leak or undersizing.
-XX:-UseGCOverheadLimit disables the check (usually worsens things).
G1 and ZGC do not produce this message; they throw plain heap-space OOM or stall.
⚠ Follow-up traps
Should you disable the check? No, it only delays an inevitable failure with an unresponsive app.
Is it thrown by G1? No, it is specific to Parallel (and CMS before removal).
#oom#gc-overhead#parallel-gc
Q55
`OutOfMemoryError: Metaspace` rises after repeated redeployments in Tomcat. Why?
advanced
Each redeploy creates a new web-app class loader. If anything outside it keeps a reference to a class or object from the old loader (a ThreadLocal, a JDBC driver registered in DriverManager, a thread, a shutdown hook), the old loader and all its classes stay in Metaspace.
Heap dump: find multiple instances of WebappClassLoader and trace the GC root path.
Use jcmd <pid> VM.classloader_stats and GC.class_stats.
Fix: deregister drivers, stop threads, clear ThreadLocals in contextDestroyed; do not hot-redeploy in production.
⚠ Follow-up traps
Is Metaspace unlimited by default? Yes, so it grows until the container or OS limit; cap it with -XX:MaxMetaspaceSize.
Do dynamically generated proxies matter? Yes, per-call Proxy/CGLIB class generation inflates Metaspace.
#oom#metaspace#classloader-leak
Q56
`OutOfMemoryError: unable to create native thread`. What are the causes?
intermediate
The OS refused a new thread: per-process or per-user limit (ulimit -u, pid_max, cgroup pids.max) or no native memory left for the thread's stack (default 1MB virtual each).
Check thread count with jcmd <pid> Thread.print | grep -c '^"', find the leaking pool.
Reduce -Xss, bound pools, replace thread-per-task with virtual threads or a bounded executor.
A larger heap leaves less address space or memory for stacks in a fixed container.
⚠ Follow-up traps
Will increasing -Xmx help? It can make it worse by taking memory the stacks needed.
Is it a heap error? No, it is native.
#oom#threads#native-memory
Q57
`OutOfMemoryError: Direct buffer memory` with a small heap. What happened?
advanced
Direct buffers are capped by -XX:MaxDirectMemorySize, and their native memory is freed only after their small heap handle objects are collected. With low heap pressure, GC rarely runs, so native memory fills first.
Typical in Netty/NIO apps allocating per request without pooling or release (ReferenceCountUtil.release).
Calling System.gc() is the JDK's last-resort retry (disabled by -XX:+DisableExplicitGC).
Track with jcmd VM.native_memory, JFR, and Netty's leak detector (-Dio.netty.leakDetection.level=paranoid).
⚠ Follow-up traps
Why does -XX:+DisableExplicitGC aggravate it? The JDK's fallback System.gc() becomes a no-op.
Does the heap dump show direct memory? Only the handle objects, not the native bytes.
#oom#direct-memory#netty
Q58
The container is killed with exit code 137 yet the heap looks fine. Why?
intermediate
The kernel OOM killer ended the process because RSS exceeded the cgroup limit. The heap (-Xmx) was within its limit, but total footprint was not: Metaspace, thread stacks, direct buffers, code cache, GC structures and malloc fragmentation add up.
Lower -Xmx or MaxRAMPercentage to leave 25-40% headroom.
Use NMT to see JVM-internal usage; set MALLOC_ARENA_MAX=2 for glibc fragmentation.
Kubernetes shows OOMKilled in kubectl describe pod.
⚠ Follow-up traps
Why no OutOfMemoryError in the logs? The JVM got SIGKILL and could not log anything.
Does setting -Xms = -Xmx increase RSS? It commits memory eagerly (and touches it with AlwaysPreTouch), raising RSS earlier.
#containers#oomkilled#native-memory
Q59
Latency spikes every few minutes with G1 and a 16GB heap. How do you proceed?
advanced
Correlate spikes with GC logs first: evacuation pauses, mixed GCs, Full GCs, humongous allocations, or non-GC safepoints. Tune only the observed cause.
Long young pauses: reduce survivor copying, lower allocation rate, check large ParallelGCThreads mismatch with CPU quota.
Long mixed pauses: raise -XX:G1MixedGCCountTarget, lower -XX:G1HeapWastePercent tuning.
Full GCs: lower InitiatingHeapOccupancyPercent, add headroom (-XX:G1ReservePercent), find humongous objects.
If targets are < 10ms, move to ZGC/Shenandoah.
⚠ Follow-up traps
Does lowering MaxGCPauseMillis always help? It shrinks the collection set, causing more frequent GCs and risk of falling behind.
Are all pauses GC? No; check -Xlog:safepoint for non-GC VM operations.
#g1#latency#gc-tuning
Q60
How would you choose between G1, ZGC and Parallel for a new service?
intermediate
Decide by SLOs. Default to G1. Choose ZGC (or Shenandoah) when tail latency matters on large heaps and you can spare 10-20% more CPU and memory. Choose Parallel for batch where throughput dominates. Use Serial for tiny containers.
Measure with realistic load; compare p99 latency, throughput, RSS.
ZGC suits heaps from a few GB to TBs; below ~1GB the barrier cost rarely pays.
Containers with < 2 CPUs get Serial by default, so force the collector explicitly.
⚠ Follow-up traps
Does ZGC raise throughput? No, it usually trades some for latency.
Can you change GC without code changes? Yes, flags only; re-test since allocation behaviour matters.
#gc-selection#trade-offs
Q61
A `HashMap`-based cache grows without bound. What are your options?
basic
Bound the cache by size or time. Use Caffeine (maximumSize, expireAfterWrite) instead of a raw HashMap; use LinkedHashMap with removeEldestEntry for a simple LRU; avoid SoftReference as the sole policy.
Make sure the key's equals/hashCode are stable and cheap.
That LinkedHashMap is not thread-safe; wrap or use Caffeine.
⚠ Follow-up traps
Is WeakHashMap an LRU? No, entries vanish when keys lose strong refs, not by usage.
Why accessOrder = true? It reorders on get, giving LRU rather than insertion order.
#memory-leak#cache#eviction
Q62
Memory grows in a Spring app and the histogram shows millions of `char[]`/`byte[]` and `String`. What next?
intermediate
Arrays and strings are symptoms. In MAT, use the dominator tree and "merge shortest paths to GC roots" on the largest byte[] to find which owner retains them: a cache, a request log buffer, a session store, or a result-set holder.
Since Java 9, String is backed by byte[] (compact strings), hence byte[] dominates.
Duplicates: check jcmd GC.heap_dump plus MAT "duplicate classes"/string duplicates; -XX:+UseStringDeduplication helps with G1.
Look at JFR "Old Object Sample" event for leak candidates with allocation stacks.
⚠ Follow-up traps
Does intern() fix duplicates? It trades heap duplicates for pool lookups and can become a scalability bottleneck.
Why not look only at shallow size? Retained size shows what would be freed.
#memory-leak#strings#heap-dump
Q63
How do you find a deadlock in production?
basic
Take a thread dump with jstack -l <pid> or jcmd <pid> Thread.print; the JVM reports "Found one Java-level deadlock" with the cycle of monitors and owners. Take multiple dumps spaced seconds apart to confirm threads are stuck, not progressing.
"worker-1" waiting to lock monitor 0x...(object A), which is held by "worker-2""worker-2" waiting to lock monitor 0x...(object B), which is held by "worker-1"
Fix with a consistent lock ordering, tryLock with timeout, or by shrinking lock scope.
ThreadMXBean.findDeadlockedThreads() detects it programmatically (including java.util.concurrent locks).
⚠ Follow-up traps
Does jstack without -l show ReentrantLock owners? Not fully; -l adds ownable synchronizers.
Is a thread in BLOCKED for a long time always a deadlock? No, may be contention.
#jstack#deadlock#threads
Q64
CPU is at 100% on one JVM. How do you find the hot thread?
intermediate
Map the busiest OS thread to a Java thread: find the thread ID with top -H -p <pid>, convert to hex, and look up nid=0x... in a jstack dump. Then confirm with JFR or async-profiler.
top -H -p 1234 # note TID 1260printf '%x\n' 1260 # 4ecjstack 1234 | grep -A 15 'nid=0x4ec'
If the hot thread is "GC Thread" or "C2 CompilerThread", it is GC or JIT activity, not business code.
Take several dumps; one sample can mislead.
⚠ Follow-up traps
Why hex?nid in dumps is the native thread ID in hexadecimal.
Does a flame graph from JFR show native frames? Limited; async-profiler covers native and kernel.
#cpu#jstack#profiling
Q65
A request is slow for the first minutes after deploy. What can you do?
intermediate
The JVM is interpreting and compiling; classes load lazily; caches and connection pools are cold. Mitigate with warm-up traffic or synthetic requests before readiness, CDS/AppCDS, and gradual traffic ramp.
Readiness probes should wait until warm-up completes.
Options: Class Data Sharing archives, CRaC checkpoints, GraalVM native image for short-lived services.
Increase compiler threads (-XX:CICompilerCount) if CPU quota is tiny and compilation starves.
⚠ Follow-up traps
Will -Xshare:auto remove JIT cold start? No, CDS helps class loading, not compilation.
Does -XX:TieredStopAtLevel=1 help? It speeds startup and reduces memory but caps peak throughput.
#warmup#jit#startup
Q66
What will this code do under escape analysis, and how would you check?
advanced
After warm-up, C2 inlines sum, sees Pair does not escape, and scalar-replaces it: zero allocation per call. When the pair is stored into a field, it escapes and allocates.
record Pair(int a, int b) {}static int sum(int x, int y) { Pair p = new Pair(x, y); return p.a() + p.b(); }
Verify with JMH -prof gc (gc.alloc.rate.norm near 0 B/op) or by comparing with -XX:-DoEscapeAnalysis.
Output: sum(2, 3) returns 5 regardless; only performance differs.
⚠ Follow-up traps
Will it hold if sum is too big to inline? No, the object then escapes at the call boundary.
Does escape analysis remove the object in the interpreter? No.
#escape-analysis#jmh#allocation
Q67
What does this `WeakReference` code print?
basic
Typically true then null after System.gc(), because the only strong reference was dropped. The output is not guaranteed since System.gc() is a hint, though HotSpot honors it by default.
Object o = new Object();WeakReference<Object> w = new WeakReference<>(o);System.out.println(w.get() != null); // trueo = null;System.gc();System.out.println(w.get()); // usually null
With -XX:+DisableExplicitGC or in a JIT-optimized loop it may still be alive.
Never base program logic on this behaviour.
⚠ Follow-up traps
What if o was a string literal? The pool keeps a strong reference, so it never clears.
Would a SoftReference clear here? No, not until memory pressure.
#weak-reference#gc#references
Q68
Design a cache of large images that should shrink under memory pressure. Which reference type?
intermediate
SoftReference fits "free when memory is tight", but the policy is JVM-controlled (LRU by time since last access scaled by free heap: SoftRefLRUPolicyMSPerMB, default 1000ms per free MB). It can cause GC thrashing and unpredictable hit rates.
Better: bounded Caffeine cache with weight-based eviction (maximumWeight) and optional softValues().
Soft refs add GC reference-processing cost and survive many young GCs, bloating old gen.
ZGC and G1 clear soft refs before throwing OOM.
⚠ Follow-up traps
Are soft refs cleared before OOM? Yes, the JVM guarantees that.
Will soft refs behave the same across servers? No, they depend on heap size and free memory.
#soft-reference#cache#design
Q69
What happens in this generics code and why?
intermediate
It compiles but throws ArrayStoreException at runtime, since arrays are covariant and reified while generics are invariant.
Object[] arr = new String[1];arr[0] = 1; // ArrayStoreExceptionList<Object> list = new ArrayList<String>(); // compile error
Arrays check the element type on every store; generics are checked only at compile time.
Mixing arrays and generics is the source of "generic array creation" errors.
⚠ Follow-up traps
Can you create T[] safely? Via (T[]) Array.newInstance(cls, n) or Object[] cast with care.
Does List<Object> = new ArrayList<String>() compile via raw types? Only with unchecked warnings.
#generics#covariance#arrays
Q70
This method signature fails to compile: `void m(List<String> a)` and `void m(List<Integer> a)`. Why?
basic
After erasure both methods have the signature m(List), a "name clash: same erasure". Overloading cannot distinguish them.
Workarounds: rename the methods, add a distinguishing parameter, or use a wrapper type.
The same erasure rule makes instanceof List<String> illegal.
⚠ Follow-up traps
Does a differing return type fix it? No, return type is not part of the Java signature (although bytecode permits it).
Is List<?> vs List<String> distinguishable? Both erase to List, so no.
#generics#erasure#overloading
Q71
A library works on Java 8 but fails on Java 17 with `InaccessibleObjectException`. What now?
intermediate
The library reflectively accesses non-open JDK internals, blocked by strong encapsulation since Java 16/17. Upgrade to a version that supports modern Java; as a stop-gap add --add-opens.
Find offenders with jdeps --jdk-internals app.jar.
Put the flags in JAVA_TOOL_OPTIONS or the manifest Add-Opens attribute.
⚠ Follow-up traps
Does --illegal-access=permit still work? No, it was removed in Java 17.
Will setAccessible(true) on a private JDK field work? Only if the package is open to the caller.
#jpms#add-opens#migration
Q72
What happens when two JARs contain the same class, and how do class loaders resolve it?
intermediate
On the classpath the first JAR in order wins; the other is silently shadowed. This produces NoSuchMethodError, NoClassDefFoundError or odd behaviour when versions differ ("JAR hell").
Diagnose with -verbose:class, -Xlog:class+load, mvn dependency:tree, or Class.getProtectionDomain().getCodeSource().
Fix with dependency convergence/dependencyManagement, shading with relocation, or module path (rejects split packages).
⚠ Follow-up traps
Does the order depend on the OS? It follows classpath order, but wildcard expansion (lib/*) order is unspecified.
Why NoSuchMethodError instead of a compile error? The compile-time version had the method, the runtime one did not.
#classpath#classloader#jar-hell
Q73
How would you write a custom class loader, and what are the risks?
advanced
Extend ClassLoader, override findClass (not loadClass, to keep parent delegation), read bytes and call defineClass.
Risks: class loader leaks, duplicate class identities, LinkageError if defined twice, security.
Unloading requires dropping all references to the loader, its classes and instances.
⚠ Follow-up traps
Why not override loadClass? You would break delegation unless careful.
Can you unload a single class? No, only via collecting its loader.
#classloader#defineclass#isolation
Q74
What does this static initialization order print?
intermediate
Output is A, B, ctor: the superclass static block runs first, then the subclass static block, only when the class is first used; the instance constructor runs after.
class P { static { System.out.print("A "); } }class C extends P { static { System.out.print("B "); } C() { System.out.print("ctor "); }}new C(); // A B ctor
A second new C() prints only ctor.
C.CONSTANT for a compile-time constant would trigger none.
⚠ Follow-up traps
What if the static block throws?ExceptionInInitializerError, then NoClassDefFoundError on every later use.
Is initialization thread-safe? Yes, the JVM locks per class; circular dependencies can deadlock.
#class-initialization#clinit#static
Q75
A benchmark shows `list.stream().mapToInt(...).sum()` is 10x faster than the boxed version. Why, and is the benchmark credible?
intermediate
Primitive streams avoid boxing and unboxing, objects allocation and pointer chasing. The gap is plausible for tight loops but must be validated with JMH, including allocation rates, since a naive timer may reflect JIT warm-up.
Use @Benchmark returning the result; inspect -prof gc for B/op.
Real gains shrink when the source is already List<Integer> (unboxing remains).
The boxed pipeline can be partially optimized by escape analysis for small cases.
⚠ Follow-up traps
Does the result generalize to I/O-heavy code? No, it matters for CPU-bound hot loops.
Is Stream always slower than for? Not always; for large pipelines after inlining they can match.
#boxing#streams#jmh
Q76
Two threads each update their own `volatile long`, yet throughput halves with both running. Explain and fix.
advanced
Both fields are adjacent in memory within one cache line, so each write invalidates the other core's copy: false sharing. Pad or isolate them.
class Counters { @jdk.internal.vm.annotation.Contended volatile long a; @jdk.internal.vm.annotation.Contended volatile long b;}
@Contended outside the JDK needs -XX:-RestrictContended and --add-exports java.base/jdk.internal.vm.annotation=ALL-UNNAMED.
Portable alternative: manual padding with unused long fields (JIT may remove or reorder them) or arrays with stride of 8-16 elements.
Best: use LongAdder, or aggregate per-thread then merge.
⚠ Follow-up traps
Do read-only shared lines have the issue? No; only writes invalidate lines.
How big is a cache line? Usually 64 bytes (128 on some ARM/Apple parts).
#false-sharing#contended#performance
Q77
You see `Allocation Stall` in ZGC logs. What do you do?
advanced
Application threads are blocked waiting for memory because allocation outpaced concurrent reclamation. Give ZGC more headroom or concurrency, or cut allocation.
Increase -Xmx (ZGC needs spare heap, often 20-30%) or -XX:SoftMaxHeapSize tuning.
More GC threads: -XX:ConcGCThreads; ensure the container has enough CPU.
Use generational ZGC (-XX:+ZGenerational on 21) to reclaim short-lived objects cheaply.
Reduce allocation rate with JFR allocation profiling.
⚠ Follow-up traps
Is this a pause? It is a per-thread stall, not a STW pause, but similarly visible to users.
Does ZGC SoftMaxHeapSize hard-limit the heap? No, it is a soft target.
#zgc#allocation-stall#tuning
Q78
A "Pause Full (G1 Compaction Pause)" appears. How do you prevent it?
advanced
A Full GC means G1 could not reclaim space fast enough: evacuation failure, humongous fragmentation or marking started too late. Fix the cause.
Start marking earlier: lower -XX:InitiatingHeapOccupancyPercent (or keep adaptive IHOP and add reserve -XX:G1ReservePercent).
Add heap or reduce allocation rate and promotion (fewer long-lived temporary objects).
Remove/size humongous objects; avoid System.gc() calls and RMI periodic GC (-XX:+DisableExplicitGC or -XX:+ExplicitGCInvokesConcurrent).
Log: -Xlog:gc*,gc+phases=debug; look for "To-space exhausted".
⚠ Follow-up traps
Is "to-space exhausted" fatal? No, it triggers slow evacuation failure handling or Full GC, but not OOM by itself.
Does Full GC use many threads in G1? Yes since Java 10.
#g1#full-gc#tuning
Q79
After upgrading to Java 17 in Kubernetes, the app uses only 1 CPU worth of threads. Why?
intermediate
The JVM derives availableProcessors() from the cgroup CPU limit (quota/period, rounded up). A limit of 500m yields 1 processor, so the common ForkJoin pool, GC threads and compiler threads shrink, and with under 2 CPUs the JVM picks Serial GC.
Check: jcmd <pid> VM.info or print Runtime.getRuntime().availableProcessors().
Fix: raise the CPU limit, force -XX:+UseG1GC and -XX:ActiveProcessorCount=N where justified.
CPU limits also throttle (CFS) bursts, causing latency spikes even when average usage is low.
⚠ Follow-up traps
Do CPU requests change ergonomics? No, only limits do (and cpusets).
Is throttling visible in GC logs? Indirectly as long pauses with low CPU time (real >> user+sys).
#containers#cpu#ergonomics
Q80
A JMH benchmark returns 0.3 ns/op for a computation. What went wrong?
intermediate
The JIT eliminated the work: either the result is unused (dead-code elimination) or the inputs are compile-time constants (constant folding). Fix by returning the value or passing it to a Blackhole, and reading inputs from non-final @State fields.
@State(Scope.Thread)public class B { double x = 42.0; // not final: prevents folding @Benchmark public double sqrt() { return Math.sqrt(x); }}
Sub-nanosecond results per op are a red flag.
Verify via -prof perfasm or -prof gc that the code is really executed.
⚠ Follow-up traps
Does final double x matter? Yes, it can be folded away.
Does returning void with Blackhole.consume cost? A little, but it is the intended guard.
#jmh#dead-code#constant-folding
Q81
Your JMH numbers vary wildly between runs. What would you check?
intermediate
Check forks (use at least 2-3), warm-up length, CPU frequency scaling/turbo, noisy neighbours, thermal throttling, GC activity, and profile pollution from polymorphic call sites.
Use @Fork(3), sufficient @Warmup, and examine the score error (±) and per-fork results.
Pin to cores (taskset), disable turbo/power saving, run on a quiet machine, avoid laptops on battery.
Run -prof gc to correlate with allocation; try -XX:+UseParallelGC or -XX:+UseEpsilonGC to isolate GC noise.
⚠ Follow-up traps
Is a bigger iteration count always better? Not if the variance comes from forks, not iterations.
Does Epsilon GC suit all benchmarks? Only short ones that fit the heap; it never reclaims.
#jmh#variance#benchmarking
Q82
A `ConcurrentModificationException` vs a stale read: how does the JMM explain a loop that never exits?
intermediate
Without volatile or synchronization, the JIT may hoist the field read out of the loop, so the reader never sees the writer's update and spins forever.
class Worker { boolean stop; // should be volatile void run() { while (!stop) { /* work */ } } void halt() { stop = true; }}
Make stopvolatile, or use AtomicBoolean/interruption.
It may appear to work in the interpreter or debug mode and fail after JIT compilation.
⚠ Follow-up traps
Will Thread.sleep in the loop fix it? Not guaranteed by the spec; use proper synchronization.
Does printing inside the loop mask the bug? Yes, System.out synchronizes and creates happens-before.
#jmm#visibility#volatile
Q83
A singleton with double-checked locking occasionally returns a partially built object. Why?
advanced
Without volatile, the write of the reference may become visible before the writes inside the constructor (reordering), so another thread sees a non-null reference to an incompletely initialized object.
class S { private static volatile S inst; // volatile is required static S get() { S s = inst; if (s == null) { synchronized (S.class) { s = inst; if (s == null) inst = s = new S(); } } return s; }}
Simpler options: the initialization-on-demand holder idiom or an enum singleton.
If all fields are final, the object is safe even without volatile.
⚠ Follow-up traps
Did the old pre-Java 5 JMM allow DCL? No, it was broken; Java 5 fixed volatile.
Does the holder idiom need locking? No, class initialization provides it.
#jmm#dcl#safe-publication
Q84
This thread dump shows 200 threads `WAITING` on `LinkedBlockingQueue.take` and 50 `BLOCKED` on one monitor. What is the diagnosis?
intermediate
Idle pool workers waiting on the queue are normal. The 50 BLOCKED threads contending for one synchronized monitor indicate a lock bottleneck, so find the single owner thread ("locked <0x...>") and what it is doing.
Look at the owner's stack: usually slow I/O, a DB call, or heavy computation inside the critical section.
Fix: shrink the critical section, move I/O outside, use finer-grained locks, ConcurrentHashMap, or read-write/stamped locks.
JFR jdk.JavaMonitorEnter events show contention totals.
⚠ Follow-up traps
Is WAITING the same as BLOCKED? No; BLOCKED is waiting to enter a monitor, WAITING is wait/park/join.
One dump enough? No, take 3-5.
#jstack#contention#thread-dump
Q85
Heap usage after each full GC climbs 50MB per hour. Where do you begin?
intermediate
Slow steady growth after full GC confirms a leak. Use JFR's Old Object Sample (low overhead) in production or two GC.class_histogram snapshots hours apart, then take a heap dump for the growing class.
Examine the OldObjectSample events with root paths in JMC.
In MAT compare dumps via "compare to baseline".
Review recent changes for new caches, listeners, metrics tags with unbounded cardinality.
⚠ Follow-up traps
Why use :live or full GC before histogram? To exclude garbage not yet collected.
Can a metrics library leak? Yes, unbounded label values create an ever-growing registry.
#memory-leak#jfr#heap-histogram
Q86
A listener registration leaks the UI/service object. How do you fix it?
basic
A long-lived publisher holds a strong reference to the listener, which holds its owner. Deregister in a lifecycle hook (close, @PreDestroy) or store listeners weakly.
class Bus { private final List<Consumer<String>> subs = new CopyOnWriteArrayList<>(); Runnable subscribe(Consumer<String> c) { subs.add(c); return () -> subs.remove(c); }}
Return an unsubscribe handle so callers can clean up.
Beware lambdas and inner classes capturing this.
⚠ Follow-up traps
Does a method reference capture this? Yes if bound (obj::method).
Are weak listeners safe? They can disappear while still needed if nothing else holds them.
#memory-leak#listeners#lifecycle
Q87
A Spring app in production generates millions of proxy classes and Metaspace grows. Cause?
advanced
Something creates a new class on every call: building a new Proxy, CGLIB Enhancer, Groovy/JS script, or MethodHandle/lambda spinner per request without caching, or a fresh class loader each time.
Heap/class histogram: many classes with names like $$EnhancerByCGLIB$$ or $Proxy123.
jcmd <pid> VM.classloader_stats; -Xlog:class+load shows the stream.
Fix by caching generated classes/proxies and reusing factories.
⚠ Follow-up traps
Does -XX:MaxMetaspaceSize fix it? It converts slow growth into an OOM.
Do reflection Method.invoke calls generate classes? Older JDKs inflated accessors after 15 calls; Java 18+ uses method handles.
#metaspace#proxies#classloading
Q88
Why does `jmap -dump:live` hurt production, and what is a safer alternative?
intermediate
:live triggers a full GC and the dump itself pauses all threads for the time it takes to write the heap, from seconds to minutes for big heaps, plus disk usage equal to the live set.
Alternatives: jcmd GC.class_histogram for a cheap overview; JFR Old Object Sample; take the dump from one instance after removing it from the load balancer.
Ensure free disk, compress the dump (gzip), and protect it as it contains secrets.
-XX:+HeapDumpOnOutOfMemoryError captures the state at failure.
⚠ Follow-up traps
Does a dump contain passwords? Yes, whatever is in memory, including keys and tokens.
Are dumps from non-live mode useful? Yes, but include garbage, making analysis noisier.
#heap-dump#jmap#production
Q89
A service on 8GB container, `-Xmx6g`, G1, gets OOMKilled at 7.9GB RSS. What do you change?
intermediate
Heap max of 75% leaves too little for off-heap use (Metaspace ~150MB, code cache up to 240MB, stacks, direct buffers, G1 remembered sets ~10%). Reduce the heap and measure the footprint.
Use NMT to find the largest non-heap consumer; cap it explicitly.
Reduce thread counts; tune MALLOC_ARENA_MAX.
⚠ Follow-up traps
Does -Xms below -Xmx reduce the risk? Only temporarily; heap grows toward Xmx.
Is G1 overhead relevant? Yes, remembered sets and card tables take ~5-20% of heap.
#containers#oomkilled#sizing
Q90
You must call a C library from Java 21 without JNI glue. How?
advanced
Use the Foreign Function and Memory API (preview in 21, final in 22): look up the symbol, create a downcall handle, and allocate arguments in an Arena. jextract can generate Java bindings from the header.
No C compilation step is needed; memory safety checks apply to segments, not to the C library's own bugs.
Close the Arena to free off-heap memory deterministically.
⚠ Follow-up traps
Does FFM make native calls safe? No, bad function descriptors or pointers can still crash the JVM.
Why call invokeExact carefully? Casts must match the descriptor exactly or it throws WrongMethodTypeException.
#panama#ffm#native
Q91
A modular app fails with `module not found: java.sql` using `jlink`. Why?
intermediate
jlink includes only the modules you list or that are resolved from your module graph. Missing requires java.sql, or services/reflection-only dependencies that static analysis cannot see, produce missing modules at runtime.
Add modules used via reflection, ServiceLoader or JNDI manually (--add-modules).
Spring Boot apps often need jdk.unsupported, java.naming, java.management.
⚠ Follow-up traps
Can jlink use automatic modules? No, all must be named modules.
Does a trimmed runtime reduce attack surface? Yes, and image size, but patches need a rebuild.
#jpms#jlink#runtime-image
Q92
A `ServiceLoader` cannot find a provider after modularization. What is missing?
intermediate
In a named module the consumer needs uses <service> and the provider needs provides <service> with <impl> in its module-info.java; the META-INF/services file is only honored on the classpath or in automatic modules.
Provider must also be resolved into the module graph (--add-modules or required).
The service interface package must be exported.
⚠ Follow-up traps
Does requires the provider create the link? Not needed, but it must be in the graph.
Can the impl have a public provider() method? Yes, a static provider method is allowed instead of a public constructor.
#jpms#serviceloader#spi
Q93
A class compiled with `List<? extends Number>` is called with `add(Integer)` and fails to compile. How do you design the API?
intermediate
If the method both reads and writes, a wildcard is the wrong tool; use a type parameter. If it only reads use ? extends, if it only writes use ? super.
static double sum(List<? extends Number> xs) { double s = 0; for (Number n : xs) s += n.doubleValue(); return s; }static void fill(List<? super Integer> out) { for (int i = 0; i < 3; i++) out.add(i); }static <T> void swap(List<T> l, int i, int j) { T t = l.get(i); l.set(i, l.get(j)); l.set(j, t); }
Wildcard capture helpers allow List<?> signatures to call generic implementations.
Return types should rarely be wildcards.
⚠ Follow-up traps
Why does List<?> swap not compile? Capture of ? is not nameable; delegate to a generic helper.
Can fill accept List<Object>? Yes, Object is a supertype of Integer.
#generics#pecs#api-design
Q94
What is the output and why? Streams of boxed values compared with `==`.
basic
true for 127 and false for 1000 on a default JVM, because Integer.valueOf caches -128..127 and returns distinct objects beyond.
Integer a = 127, b = 127;Integer c = 1000, d = 1000;System.out.println(a == b); // trueSystem.out.println(c == d); // falseSystem.out.println(c.equals(d)); // true
-XX:AutoBoxCacheMax=2000 would flip the second to true; never rely on it.
Always compare boxed values with equals or unbox.
⚠ Follow-up traps
Does the cache cover Long? Yes, -128..127 as well, but not Double.
Does c == 1000 compare references? No, mixing with a primitive unboxes c.
#boxing#integer-cache#equality
Q95
A hot loop adds `Long` values into `Map<String, Long>` via `merge`. How would you reduce GC pressure?
intermediate
Every Long above 127 allocates. Switch to a primitive-specialized map (fastutil Object2LongOpenHashMap, Eclipse Collections), or hold a mutable accumulator: Map<String, LongAdder> or long[] slot.
Map<String, long[]> m = new HashMap<>();m.computeIfAbsent(key, k -> new long[1])[0] += delta;
Measure with JMH -prof gc to see B/op before and after.
Avoid premature optimization if allocation rate is small.
⚠ Follow-up traps
Is long[] thread-safe? No; use LongAdder for concurrent updates.
Does merge(key, 1L, Long::sum) allocate? Yes for values beyond the cache.
#boxing#allocation#collections
Q96
A CPU-bound service has high young GC frequency but low pause times. Is that a problem?
intermediate
Not necessarily. High allocation rate with short young pauses is healthy generational behaviour, provided survivors die and promotion is low. It becomes an issue when GC CPU share is high, young gen is too small causing premature promotion, or pauses breach SLOs.
Check GC time percentage, promotion rate, survivor occupancy and old growth.
Increasing young generation size reduces frequency at the expense of per-GC work.
⚠ Follow-up traps
Do frequent young GCs imply a leak? No, only old gen growth does.
Will G1 grow young gen automatically? Yes, within G1NewSizePercent/G1MaxNewSizePercent to meet the pause target.
#gc#allocation-rate#young-gen
Q97
Phantom references: how would you implement safe cleanup of a native handle?
advanced
Use Cleaner with a static nested cleanup action that holds only the native handle (not the owner), and provide AutoCloseable for deterministic release.
final class NativeRes implements AutoCloseable { private static final Cleaner CLEANER = Cleaner.create(); private record State(long handle) implements Runnable { public void run() { free(handle); } } private final Cleaner.Cleanable cleanable; NativeRes(long h) { cleanable = CLEANER.register(this, new State(h)); } @Override public void close() { cleanable.clean(); } // idempotent private static void free(long h) { /* native free */ }}
If State were an inner class referencing NativeRes, the object would never become phantom reachable.
clean() runs at most once.
⚠ Follow-up traps
Why avoid a lambda capturing this? It keeps the owner strongly reachable.
Is the Cleaner thread guaranteed to run promptly? No; treat it as a safety net.
#cleaner#phantom-reference#native-resources
Q98
A request handler stores user context in a `ThreadLocal` and values leak across requests. What happened?
intermediate
Pool threads are reused. If the value is not removed after a request, the next request on that thread sees stale data (a security bug) and the value stays reachable (a leak).
static final ThreadLocal<User> CURRENT = new ThreadLocal<>();void handle(Req r) { CURRENT.set(auth(r)); try { process(r); } finally { CURRENT.remove(); }}
Put cleanup in a servlet Filter/interceptor finally.
InheritableThreadLocal copies to child threads at creation, and does not follow pool-reused threads.
Does set(null) equal remove()? It leaves the entry in the map; remove deletes it.
Does @Async copy the ThreadLocal? No, unless you configure a TaskDecorator.
#threadlocal#thread-pool#bug
Q99
After enabling `-XX:+UseStringDeduplication` memory barely drops. Why?
advanced
Deduplication shares only the underlying byte[] of strings that survive a few GCs (age threshold), and runs concurrently in the background. It does not dedup String objects themselves nor short-lived strings, and has no effect if the duplicates are not strings or the bytes are tiny.
Observe with -Xlog:stringdedup*=debug.
Better wins come from avoiding duplication at the source (interning controlled sets, enums, canonical maps).
Supported by G1, ZGC (generational), Shenandoah, Parallel and Serial in recent JDKs.
⚠ Follow-up traps
Is it the same as String.intern? No; intern returns the canonical String, dedup merges the arrays.
Does it cost CPU? Yes, a small background cost.
#string-dedup#g1#memory
Q100
You're asked to eliminate a 3-second Full GC in a 64GB Parallel GC batch service that serves user requests. Options?
advanced
Parallel's full pause scales with live data. Move to a concurrent collector: G1 first (Full GC avoided, pauses ~200ms), ZGC or Shenandoah if you need < 10ms. Validate under load, compare throughput loss, memory overhead and CPU.
Heaps above 32GB lose compressed oops; ZGC does not use them anyway.
Alternatively split the batch and the request-serving workloads into separate JVMs.
⚠ Follow-up traps
Will ZGC remove all pauses? No, but they are sub-millisecond typically.
Is it safe to flip without testing? No; throughput and memory footprint change.
#gc-selection#latency#migration
Q101
A thread dump shows many threads in `RUNNABLE` state inside `socketRead0`. Is the CPU busy?
intermediate
Not necessarily. Threads blocked in native I/O such as socketRead0 are reported RUNNABLE by the JVM, because it does not distinguish them from running Java code. They are waiting on the network, typically a slow downstream, without timeout.
Verify with top -H and per-thread CPU time.
Fix: set connect/read timeouts, use circuit breakers and bulkheads, and monitor pool saturation.
JFR jdk.SocketRead events show durations and hosts.
⚠ Follow-up traps
Do RUNNABLE threads count as CPU usage? No.
Does a virtual thread blocked on a socket pin a carrier? No, the JDK unmounts it for java.net/NIO blocking calls.
#jstack#runnable#io
Q102
Your app logs `Compilation of ... made not entrant` and gets slower. What is happening?
advanced
Deoptimization: a speculative assumption (type profile, class hierarchy, null/range check, uncommon trap) failed, so compiled code was invalidated ("made not entrant") and execution fell back to the interpreter until recompiled. Occasional events are normal; repeated cycles hurt performance.
Typical triggers: a new subclass loaded changing a monomorphic call site, a branch never taken before now executed.
Inspect with -XX:+UnlockDiagnosticVMOptions -XX:+LogCompilation or JFR jdk.Deoptimization.
Mitigate by warming with representative traffic and avoiding megamorphic hot paths.
⚠ Follow-up traps
Is deopt a bug? No, it is how speculative optimization stays correct.
Can warm-up with unrealistic data hurt? Yes, it builds wrong type profiles.
#jit#deoptimization#profiling
Q103
JIT stops compiling and `CodeCache is full` is logged. What is the impact?
advanced
When the code cache fills, the JIT disables compilation ("CodeCache is full. Compiler has been disabled"), so new hot code runs interpreted, producing a drastic slowdown and CPU rise.
Increase -XX:ReservedCodeCacheSize (default 240MB with tiered compilation).
Code cache sweeping/flushing exists but cannot always cope with heavy dynamic class generation.
Monitor with jcmd <pid> Compiler.codecache and the JMX Code Cache pool.
⚠ Follow-up traps
Is the code cache part of the heap? No, it is native, counted in RSS.
Is reducing tiers an option? Yes, -XX:TieredStopAtLevel=1 shrinks code size but loses peak performance.
#jit#codecache#tuning
Q104
You need to prove that a refactor reduced allocations. How do you do it rigorously?
intermediate
Write a JMH benchmark for the hot path and use the GC profiler, which reports normalized allocation per operation (gc.alloc.rate.norm) independent of speed. Compare before and after with several forks.