Share an async-profiler report

async-profiler is the default answer for "why is this JVM slow" because it samples without the safepoint bias that makes most Java profilers agree with each other and disagree with reality. Since 2.x it renders its own flame graph directly:

# attach to a running JVM for 30 seconds of CPU samples
asprof -e cpu -d 30 -f profile.html 12345

# or start with the agent
java -agentpath:/opt/async-profiler/lib/libasyncProfiler.so=start,event=cpu,file=profile.html \
     -jar service.jar

The output is one self-contained HTML file with the graph and its interaction layer inlined. That makes it trivial to produce and awkward to deliver: too big to paste, downloaded rather than opened when attached to a ticket, and rendered as a flat preview by chat tools — which removes the zoom and the search box, which is the entire reason to look at a flame graph.

Publish it

Drag profile.html into the app, or POST it:

curl -fsS -X POST "https://commareports.com/api/v1/reports" \
  -H "Authorization: Bearer $COMMA_API_TOKEN" \
  -H "Content-Type: application/json" \
  -d "$(jq -n --rawfile html profile.html \
        --arg t "async-profiler — orders-api, 14:05 UTC" '{title: $t, html: $html}')"

Report HTML renders with scripts enabled inside a sandboxed iframe (allow-scripts, no allow-same-origin), so clicking a frame zooms, the reset link works, and Ctrl-F highlights every matching stack — at the URL, for everyone, including the person who joins the incident an hour later.

Pick the event before you pick the tool

The most common wasted profile is a CPU profile of a process that is not CPU bound. Match the event to the symptom:

Symptom Event
High CPU, high load average -e cpu
GC pressure, rising heap -e alloc
Threads idle, throughput flat -e lock
Slow, but nothing is busy -e wall -t

-e wall -t (wall clock, per thread) is the one that finds the thread parked on a downstream HTTP call — invisible in every CPU profile you will take first.

Keep one URL per investigation

Re-profiling is the normal shape of the work: capture, change a setting, capture again. PATCH the same report id:

curl -fsS -X PATCH "https://commareports.com/api/v1/reports/$REPORT_ID" \
  -H "Authorization: Bearer $COMMA_API_TOKEN" \
  -H "Content-Type: application/json" \
  -d "$(jq -n --rawfile html profile.html \
        --arg t "async-profiler — orders-api, after pool resize" '{title: $t, html: $html}')"

The incident channel keeps one link, and the revision list is the timeline.

What review adds

  • Anchored threads on the hot frame — see commenting on HTML.
  • Revisions, so the before/after pair is one artifact.
  • Access per report — JVM stacks expose package names, vendored library versions and internal service names. See the sharing model.

Limits

  • Entry HTML: 5 MB. A 30-second capture sits well inside it; very long wall captures across a large thread pool are the ones to shorten.
  • 60 requests/minute per token.

Try it

Comma is free — unlimited reports, unlimited commenters, unlimited revision history.

Publish a JVM profile →

Related