Skip to main content

Java profiling

Coroot can profile Java applications using async-profiler, a low-overhead profiler for HotSpot JVMs. When enabled, it captures CPU, memory allocation, and lock contention profiles without requiring any changes to the Java application.

How it works

Coroot's node agent dynamically loads async-profiler into running Java processes using the JVM Attach API. No JVM flags, no application restarts, and no Java agent JARs are needed.

The profiling lifecycle:

  1. Detection: The agent discovers HotSpot JVMs by scanning /proc/<pid>/maps for libjvm.so.
  2. Deployment: The agent deploys libasyncProfiler.so (~600KB) into the container's filesystem at /tmp/coroot/.
  3. Start: The agent loads the native library into the JVM via the attach protocol, starting CPU, allocation, and lock profiling.
  4. Collection: Every 60 seconds, the agent stops the profiler (finalizing the JFR output file), reads the data, parses it, and immediately starts a new session. The gap is ~4ms.
  5. Upload: Parsed profiles are uploaded to Coroot in pprof format.

Supported profile types

ProfileDescriptionEvent
Java CPUCPU time attribution per stack traceitimer (no safepoint bias)
Java Memory (alloc_space)Bytes allocated per stack traceTLAB allocation events
Java Memory (alloc_objects)Object count per stack traceTLAB allocation events
Java Lock (delay)Time spent waiting for locksJavaMonitorEnter (threshold: 10ms)
Java Lock (contentions)Number of lock contentionsJavaMonitorEnter (threshold: 10ms)

Enabling

Set the --enable-java-async-profiler flag (or ENABLE_JAVA_ASYNC_PROFILER=true environment variable) on the node agent.

In the Coroot custom resource:

apiVersion: coroot.com/v1
kind: Coroot
spec:
nodeAgent:
env:
ENABLE_JAVA_ASYNC_PROFILER: "true"

Metrics

When async-profiler is enabled, the following Prometheus metrics are exported per JVM:

MetricTypeDescription
container_jvm_alloc_bytes_totalCounterTotal bytes allocated
container_jvm_alloc_objects_totalCounterTotal objects allocated
container_jvm_lock_contentions_totalCounterTotal lock contentions
container_jvm_lock_time_seconds_totalCounterTotal time waiting for locks
container_jvm_profiling_statusGauge1 if async-profiler is enabled

These metrics are derived from the same profiling data and provide time-series visibility into allocation rates and lock contention. Use them alongside the flamegraph profiles to spot anomalies and drill into the details.

Compatibility

  • JVM: HotSpot-based JVMs (OpenJDK, Oracle JDK, Amazon Corretto, etc.). OpenJ9 is not supported.
  • JDK version: 8+ (11+ recommended)
  • Architecture: x86_64 and ARM64
  • Privileges: No special capabilities required (no perf_event_open, no CAP_PERFMON)

Conflict detection

If another tool (e.g., Pyroscope Java agent, Datadog) has already loaded async-profiler into a JVM, Coroot's agent detects this by scanning /proc/<pid>/maps and skips that process to avoid conflicts.

Overhead

  • CPU overhead: ~1-2% per profiled JVM
  • Memory: async-profiler uses an in-memory JFR buffer (bounded)
  • Agent-side: Parsing ~1MB of JFR data takes ~200-400ms per JVM per collection cycle
  • Profiling gap: ~4ms every 60 seconds (during stop/start transition)
Looking for 24/7 support from the Coroot team? Subscribe to Coroot Enterprise:Start free trial