async-profiler: Low-Overhead JVM Profiling
async-profiler samples a running JVM for CPU, allocation, and lock contention with low overhead and can render the result as a flame graph.
async-profiler avoids the safepoint bias of many Java profilers by using perf events and AsyncGetCallTrace. It produces flame graphs directly and attaches to a live JVM.
What it does
async-profiler samples JVM stacks for a chosen event (cpu, alloc, lock, wall) and aggregates them into a flame graph HTML, collapsed text, or JFR file. Recent versions ship the asprof launcher; it attaches to a running JVM by pid or starts one with an agent.
Common usage
# profile CPU for 30s into a flame graph
asprof -d 30 -f flame.html <pid>
# allocation profiling
asprof -e alloc -d 30 -f alloc.html <pid>
# older invocation via the bundled script
./profiler.sh -e cpu -d 30 -f flame.html <pid>Options
| Flag | What it does |
|---|---|
| -e <event> | Event to profile: cpu, alloc, lock, wall, cache-misses |
| -d <seconds> | Profiling duration |
| -f <file> | Output file; extension picks the format (.html, .collapsed, .jfr) |
| -t | Profile per thread |
| <pid> | The target JVM process id |
In CI
CPU profiling uses perf_events, so the runner needs kernel.perf_event_paranoid set low enough (1 or less) and, in containers, --cap-add SYS_ADMIN or PERFMON. Where perf events are blocked, use the ctimer or wall engine, which do not require them. Archive the flame HTML as an artifact.
Common errors in CI
"Perf events unavailable" or "perf_event_open failed: Operation not permitted" is the paranoid/capability restriction; lower kernel.perf_event_paranoid or switch to -e wall / ctimer mode. "Target JVM failed to load libasyncProfiler.so" often means an architecture or libc mismatch (for example a glibc build on Alpine musl); use the musl build. "Could not start attach mechanism" means the JVM has -XX:+DisableAttachMechanism or a pid namespace mismatch in containers.