Performance & Optimization
Profiling
- JFR:
-XX:StartFlightRecordingorjcmd JFR.start— ~1% overhead, safe in production - async-profiler: CPU/alloc/lock flame graphs without safepoint bias
- Flame graph reading: width = time share; look for wide plateaus you own
- Execution profilers hide I/O waits — wall-clock mode or JFR events catch blocked time
- Heap analysis:
jcmd GC.heap_dump+ Eclipse MAT (dominator tree finds leaks) - Old instrumenting profilers distort hot code — trust sampling
# Flight Recorder: record 2 minutes, dump to file
$ jcmd <pid> JFR.start duration=120s filename=rec.jfr settings=profile
$ jfr print --events jdk.CPULoad,jdk.GarbageCollection rec.jfr # or open in JDK Mission Control
# async-profiler: 30 s CPU flame graph
$ asprof -d 30 -f flame.html <pid>
$ asprof -e alloc -d 30 -f alloc.html <pid> # allocation sites instead
# Emergency triage
$ jcmd <pid> Thread.print # what is everyone doing right now?
$ jcmd <pid> GC.heap_dump dump.hprofSafepoint bias (Optimizing Java ch. 13): classic samplers (JVisualVM et al.) can only sample at safepoints, so they systematically attribute time to safepoint-friendly code and miss tight JIT-compiled loops. async-profiler uses AsyncGetCallTrace + perf events to sample anywhere, which is why its flame graphs are the community standard. JFR sidesteps the issue with its own event machinery and adds the whole JVM's telemetry: GC, JIT, locks, TLAB allocations, socket I/O.
Memory-leak workflow: watch old-gen occupancy after full collections trend upward (Gc Tuning Logging); take a heap dump near fullness; open the dominator tree in MAT — it ranks objects by retained size, and the leak is usually a top-3 entry (a static map, an unbounded cache, a listener list that only grows). jmap -histo:live <pid> gives a quick class histogram without the dump ceremony.