DEV Community

Machine coding Master
Machine coding Master

Posted on

Stop Burning CPU on Continuous JFR: Trigger Ephemeral Snapshots with Micrometer in JDK 26

Stop Burning CPU on Continuous JFR: Trigger Ephemeral Snapshots with Micrometer in JDK 26

Running continuous JFR streaming on high-density JDK 26 Virtual Thread pools wastes precious CPU cycles and floods storage with useless allocation telemetry. You don't need gigabytes of idle traces; you need an automated 5-second diagnostic snapshot precisely when tail latency spikes past your P99.9 SLA.

Shameless plug: javalld.com has full LLD implementations with step-by-step execution traces — free to use while prepping.

Why Most Developers Get This Wrong

  • Leaving continuous profile JFR streaming on in production, burning a 3-5% CPU tax on tens of thousands of uninteresting virtual thread executions.
  • Relying on periodic static thread dumps that completely miss microsecond-level carrier thread pinning and transient lock contention.
  • Storing long-running JFR recordings locally until disk space exhausts, rather than generating targeted diagnostic artifacts tied directly to trace IDs.

The Right Way

Use Micrometer Observation Handlers as real-time circuit breakers that spawn short, dynamic JFR snapshots only during tail-latency anomalies.

  • Keep a low-overhead custom ObservationHandler wired into your application's Micrometer registry.
  • Evaluate request duration on onStop() against dynamic percentile thresholds (e.g., P99.9 > 500ms).
  • Spin off an asynchronous, non-blocking 5-second JDK Recording session off the critical execution path.
  • Flush ephemeral recordings directly to object storage with contextual metadata attached.

Show Me The Code

public class JfrAnomalyHandler implements ObservationHandler<Observation.Context> {
    private static final long SLA_THRESHOLD_NS = 500_000_000L; // 500ms P99.9 SLA

    @Override
    public void onStop(Observation.Context context) {
        if (context.getDuration() != null && context.getDuration().toNanos() > SLA_THRESHOLD_NS) {
            CompletableFuture.runAsync(() -> {
                try (var rec = new Recording(Configuration.getConfiguration("profile"))) {
                    rec.start();
                    Thread.sleep(5000); // Capture 5s diagnostic window
                    rec.dump(Path.of("/tmp/jfr-p99-" + System.currentTimeMillis() + ".jfr"));
                } catch (Exception e) { /* handle telemetry failure */ }
            });
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

Key Takeaways

  • Zero Idle Tax: Pay the profiling performance penalty only when your application is actively degrading.
  • Context-Aware Diagnostics: Pinpoint exact Virtual Thread carrier starvation and pinning without sifting through gigabytes of dead telemetry.
  • Micro-Snapshots Work: A 5-second dynamic burst gives you 100% of the root-cause data needed for JDK 26 runtime issues without storage bloat.

Top comments (0)