JVM Performance — Questions with Complete Answers
The JD says "deep Java expertise (concurrent programming, JVM internals, GC behavior, heap analysis)". These are the questions that test it, with answers grounded in the Phase 08 labs.
Table of Contents
- Garbage collection
- JIT and benchmarking
- Memory model and concurrency
- Heap analysis war stories
- References
Garbage collection
Q: Compare G1, ZGC, and Parallel GC. When would you pick each for a data-infrastructure JVM?
Parallel GC maximizes throughput with stop-the-world collections — right for batch jobs (Spark executors doing CPU-bound work) where a 500 ms pause is irrelevant. G1 targets predictable pauses by collecting region-by-region with a pause-time goal — the default general-purpose choice for services and most brokers. ZGC gives sub-millisecond pauses via colored pointers and concurrent relocation at some throughput and memory cost — right for latency-SLO services with large heaps (100 GB+). For Kafka brokers, note that the page cache, not the heap, does the heavy lifting — keep heaps modest (~6 GB) regardless of collector.
Q: What is allocation rate and why does it matter more than heap size?
Bytes allocated per second drives the frequency of young collections; most "GC problems"
in data systems are allocation-rate problems (per-record garbage in hot loops: boxing,
iterator churn, string concatenation, temporary arrays). Halving allocation rate halves
young-GC frequency without touching the heap. Phase 08 Lab 02 measures exactly this:
boxing (Long vs long accumulators) and stream-vs-loop allocation profiles via
JFR/-verbose:gc.
Q: A Spark executor spends 40% of time in GC. What do you look at?
In order: (1) allocation rate and what's allocating — JFR allocation profiling or async
profiler alloc mode; usual suspects are wide-row deserialization, UDF object churn, and
collect-like operators; (2) memory config — executor memory vs spark.memory.fraction,
whether execution spills are thrashing; (3) data skew creating one giant partition (fix the
partitioning, not the GC); (4) only then GC tuning — bigger young gen for batch, or G1
region size for humongous allocations (records > 16 MB). Saying "increase the heap" first
is a junior answer; the heap often just makes pauses longer.
JIT and benchmarking
Q: Why do naive Java microbenchmarks lie? Name the specific JIT effects.
Dead-code elimination (unused results delete your benchmark body — counter with JMH
Blackhole), constant folding (compile-time-known inputs fold away — use @State fields),
loop unrolling combined with OSR producing unrepresentative compilation, warmup (first N
calls run interpreted/C1; steady state is C2), inlining differences between the benchmark
harness and real call sites, and profile pollution (a megamorphic call site in one test
deoptimizes another's monomorphic fast path). JMH exists to control all of these —
forked JVMs, warmup iterations, blackholes — which is Phase 08 Lab 01.
Q: What is a safepoint and how does it bite production systems?
A point where all threads are at known states so the VM can do stop-the-world work (GC
phases, deoptimization, biased-lock revocation, thread dumps). Threads poll for safepoint
requests; a thread in a long counted loop without a poll (classic: int-indexed hot loop)
delays everyone — time-to-safepoint spikes show up as mysterious tail latency even when
"GC pauses" look short. Diagnose with -Xlog:safepoint.
Memory model and concurrency
Q: What does volatile guarantee, and what does it not?
Visibility (a read sees the latest write, via happens-before on the volatile variable) and
ordering (no reordering of surrounding accesses across the volatile access). It does not
make compound operations atomic — count++ on a volatile is still a lost-update race. For
counters use AtomicLong (CAS) or LongAdder (striped cells); Phase 08 Lab 03 benchmarks
why: under contention, CAS retry storms make AtomicLong degrade while LongAdder scales,
at the cost of a more expensive sum().
Q: synchronized vs ReentrantLock vs StampedLock — when does each win?
synchronized: simplest, JIT-friendly (lock elision, coarsening, biased locking
historically), right default for uncontended or briefly-held locks. ReentrantLock:
tryLock with timeout, interruptible acquisition, fairness, multiple conditions — choose it
when you need those features, not for speed. StampedLock: optimistic reads make
read-mostly structures very fast, but it's non-reentrant and easy to misuse. The deeper
answer: at high contention the win is not having the lock — striping (LongAdder,
ConcurrentHashMap), immutability, or per-thread accumulation then merge.
Q: Explain a memory leak pattern specific to long-running JVM data services.
Unbounded caches keyed by something with unstable hashCode/equals; listener/callback
registration without deregistration; ThreadLocals on pooled threads (the pool keeps the
thread, the thread keeps the value); classloader leaks on redeploy (a static somewhere
holds an instance whose class came from the old loader); and accumulating metrics
registries with per-entity tags of unbounded cardinality. Heap-dump triage: dominator tree
in MAT/jhat → biggest retained sets → ask "what owns this and why is it still reachable?"
Heap analysis war stories
Use these as templates — replace with your own lab numbers.
- The boxing regression: a refactor changed a hot aggregation from
longtoLong; allocation rate ×6, young GC every 300 ms. Found via JFR allocation profile; fixed with a primitive accumulator; wrote a JMH benchmark into CI to pin it. (Phase 08 Lab 02 is this.) - The contended counter: metrics singleton with
AtomicLong.incrementAndGet()on every record; 32 cores, throughput plateaued at 8. Async-profiler showed 40% cycles in CAS retries;LongAdderrestored linear scaling. (Phase 08 Lab 03 reproduces it.) - The "clever" partitioner: bit-twiddled hash that was fast and wrong — skewed 70% of keys to 3 partitions. Chi-square test in the review caught it. (Phase 05 Lab 03 / Phase 07 Lab 02.)
References
- JMH — official samples (read all 40; they are the JIT-pitfall curriculum)
- Java Memory Model — JSR-133 FAQ
- Java Concurrency in Practice — Goetz et al.
- G1 GC tuning guide (Oracle)
- async-profiler — allocation and CPU flamegraphs
- JDK Flight Recorder docs