An order service slows down every afternoon. The team adds logging, suspects the database and doubles the heap. Three weeks later a profiler shows, in five minutes, that a regular expression is compiled on every request.
Java profilers replace that guessing with measurement. This guide covers what they measure, the best tools in 2026, and what the JDK itself now ships.
Why Java performance matters
Performance decides what a system costs and what it can promise. A slow service needs more servers to hold the same load, a nightly batch that overruns its window delays the morning's reports, and a checkout that takes three seconds at peak loses the customers who will not wait.
In some systems speed is the product. FastPost, the accounting platform we developed for Legerity, processes a billion financial transactions in under an hour (case study); numbers like that are reached by measuring every step, not by guessing which one is slow.
And guessing is the norm. Developers are often wrong about where the time goes, because the costly part is frequently a library call or a framework default nobody looked at. That is the job of a profiler.
What is a Java profiler?
A Java profiler is a tool that watches a running JVM and reports where it spends CPU time, wall-clock time (including waiting on I/O and locks), memory allocation and heap. The result points to methods, lines and call paths, so you fix the code that matters instead of the code that looks suspicious.
Most modern profilers are sampling profilers: at a fixed interval they record the stack of each thread, and after thousands of samples the busiest code paths stand out. Sampling costs little, so it can run in production. Instrumenting profilers insert timing code into methods and give exact call counts, at a higher cost that suits a test environment better.
Types of Java profiling tools
- JVM profilers attach to one process and show CPU, allocation, locks and threads in detail: async-profiler, JDK Flight Recorder, IntelliJ IDEA, JProfiler, YourKit, VisualVM.
- Heap analyzers open a heap dump after the fact to find what holds memory: Eclipse Memory Analyzer.
- Continuous profilers and APM tools profile every instance in production all the time, so you can compare today with last week: Grafana Pyroscope and the profilers built into commercial monitoring suites.
The best Java profilers in 2026
The table sums up the eight tools below. Versions and features are as published by each project in October 2026; the order is not a ranking.
| Tool | License | Best for | In production |
|---|---|---|---|
| async-profiler | Free, open source | CPU, wall-clock, allocation and lock flame graphs | Yes |
| JDK Flight Recorder and Mission Control | Free, part of the JDK | Always-on recording, JVM events, GC | Yes |
| IntelliJ IDEA Profiler | IntelliJ IDEA subscription | Profiling from the IDE while developing | Rarely |
| JProfiler | Commercial | Deep analysis with JDBC, HTTP and ORM probes | With care |
| YourKit Java Profiler | Commercial | CPU and memory analysis, broad JVM support | With care |
| VisualVM | Free, open source | A quick first look, monitoring and heap dumps | Rarely |
| Eclipse Memory Analyzer | Free, open source | Finding memory leaks in heap dumps | Offline |
| Grafana Pyroscope | Free, open source | Continuous profiling across a fleet | Yes |
async-profiler
async-profiler is the tool we reach for first. It is a low-overhead sampling profiler that avoids the safepoint bias of older JVM profilers, shows Java, native and kernel frames in one flame graph, and covers CPU, wall-clock time, allocations, locks and hardware counters. Version 4.0 added a native memory leak profiler and interactive heatmaps; the current 4.5, from July 2026, supports JDK 27 and exports in the OpenTelemetry profiles format (release notes).
Pros
- Free and open source, with overhead low enough for production.
- Attaches to a running JVM without a restart (
asprof -d 30 -f cpu.html <pid>). - Wall-clock mode shows time spent waiting, which CPU profiles miss.
- Writes JFR files, so the results open in Mission Control and IDEs.
Cons
- Linux and macOS only; no Windows build.
- Command-line first; the analysis happens in the flame graph, not in a GUI.
- Kernel stacks need perf_events permissions, which locked-down containers may not grant.
JDK Flight Recorder and JDK Mission Control
JDK Flight Recorder (JFR) is built into the JVM and has been part of OpenJDK since Java 11 (JEP 328). It records hundreds of event types, from method samples and allocations to GC phases, lock waits, socket reads and virtual thread pinning, into a file you analyze later. Its default settings are designed to stay on in production. JDK Mission Control, now at version 9.1.2 (download page), is the desktop tool for reading the recordings.
Pros
- No install and no license cost; every current JDK has it.
- Low enough overhead to run continuously as a flight recorder for incidents.
- Covers the JVM's own behavior (GC, JIT, safepoints) that other profilers only infer.
jfr viewprints ready-made tables from a recording in a terminal.
Cons
- Mission Control is a separate download and feels dated next to commercial tools.
- Its method sampling is less detailed than async-profiler's on native code.
- Recordings can hold sensitive configuration data unless you redact it (see the Java 27 change below).
IntelliJ IDEA Profiler
The profiler built into IntelliJ IDEA starts with the run configuration or attaches to a running process. It shows CPU and memory live charts, CPU and allocation profiles, memory snapshots and thread dumps, and links every frame back to the source (IntelliJ IDEA documentation). Since IntelliJ IDEA became a single product in version 2025.3, the profiler is among the features JetBrains keeps for paying subscribers (JetBrains).
Pros
- Profiling without leaving the editor, with jumps from a hot frame to the line of code.
- Good flame graph and call tree views, with a low learning curve.
- Can open JFR files recorded elsewhere.
Cons
- Requires a paid IntelliJ IDEA subscription.
- Built for local development, not for profiling production fleets.
JProfiler
JProfiler from ej-technologies is a commercial profiler with a strong GUI and probes that record JDBC statements, HTTP calls, JPA and Hibernate operations and MongoDB queries next to the CPU data. Version 16.1, released in April 2026, added an MCP server, so AI coding tools can drive a profiling session and read its results; 16.2 in July 2026 made offline profiling configurable from the command line.
Pros
- Probes connect slow requests to the SQL, HTTP or MongoDB calls behind them.
- Detailed memory views: allocation call trees, heap walker, leak comparisons.
- IDE integrations and an MCP server for AI-assisted analysis.
Cons
- Paid licenses per developer.
- Instrumentation modes add noticeable overhead; use sampling for production-like tests.
YourKit Java Profiler
YourKit is the other long-established commercial profiler. Version 2026.9, released in September 2026, supports Java 8 to Java 27 and adds AI-assisted analysis of snapshots through a built-in MCP server. It profiles CPU, memory and threads, locally or remotely.
Pros
- Broad support, from Java 8 systems to the newest release.
- Strong memory analysis and snapshot comparison for regression hunting.
- Runs on Linux, macOS and Windows, including ARM.
Cons
- Commercial license.
- The interface is dense; it takes time to learn.
VisualVM
VisualVM was bundled with the JDK up to Java 8 and has been a standalone, free project since; version 2.2.2 came out in September 2026 (releases). It monitors CPU, heap, classes and threads, takes and browses heap and thread dumps, and has a basic sampler and profiler.
Pros
- Free, simple and quick to start with.
- Good for a first look at a local or remote JVM.
Cons
- Basic profiling compared with async-profiler or the commercial tools.
- Its instrumenting profiler is too heavy for production use.
Eclipse Memory Analyzer
Eclipse Memory Analyzer (MAT), version 1.17.0 from June 2026 (download page), does one thing: it opens heap dumps, including very large ones, and finds what holds the memory. Its leak suspects report and dominator tree are usually the fastest route from an OutOfMemoryError to the collection that keeps growing.
Pros
- Free, and the standard tool for heap dump analysis.
- Handles very large dumps that GUI profilers struggle with.
Cons
- Analyzes snapshots only; no CPU profiling.
- Needs Java 21 to run and a machine with memory to match the dump.
Grafana Pyroscope
Grafana Pyroscope is a continuous profiling database. Its Java agent is built on async-profiler and sends profiles in JFR format from every instance, so you can compare flame graphs between versions, hosts or hours. The commercial monitoring suites offer the same idea inside their APM products.
Pros
- Always on, so the profile of last night's incident already exists.
- Compares releases and finds regressions across a whole fleet.
- Free and open source, with a managed option in Grafana Cloud.
Cons
- Another backend to run and store data for.
- Shows where time goes, not the request-level traces an APM tool adds.
Two older names come up in searches. The NetBeans profiler shares its engine with VisualVM, and XRebel from Perforce is a lightweight development-time profiler for web requests. Both still exist; neither is where we would start a performance investigation in 2026.
How to choose a Java profiler
- For a production problem, start with JFR or async-profiler on the running system; both are free and safe there.
- For memory leaks, take a heap dump and open it in Eclipse Memory Analyzer.
- For everyday work in the IDE, the IntelliJ IDEA Profiler, JProfiler or YourKit, whichever your team already pays for.
- For a fleet of services, add continuous profiling so the evidence is there before anyone asks for it.
Whatever the tool, profile under realistic load. A profile of an idle service shows the code that runs at startup. Our guide to improving Java performance describes the full measure-and-fix loop, and the list of common Java performance problems shows what the profiles usually reveal.
Performance tools in modern Java versions, and why upgrading pays off
Some of the most useful profiling tools are now part of the JDK itself, free and ready in every build. Each recent release added more, so the version you run decides what you can see. A team still on Java 8 or 11 investigates production problems with a fraction of the tooling the JDK now ships.
Java 9 to 11: unified logging and Flight Recorder for everyone
Java 9 replaced a dozen logging flags with one syntax, -Xlog, for every part of the JVM (JEP 158). GC, safepoints, class loading and the JIT now log the same way, with time stamps and rotation built in, which makes it practical to keep GC logging on in production:
-Xlog:gc*,safepoint:file=gc.log:time,uptime,level,tags:filecount=5,filesize=20m
Java 11 then brought JDK Flight Recorder into OpenJDK (JEP 328). Before that it was a feature of Oracle's commercial JDK. Since 11, every build can record method samples, allocations, GC phases, lock waits, socket and file I/O and dozens of JVM internals, and the default configuration aims to cost less than 1% of CPU. Recording can start with the JVM or be switched on later, with no restart:
java -XX:StartFlightRecording=filename=app.jfr,dumponexit=true -jar app.jar
jcmd <pid> JFR.start name=incident maxage=30m # keep a rolling 30-minute window
jcmd <pid> JFR.dump name=incident filename=incident.jfr
The rolling window is the habit worth adopting. When an incident happens, the last half hour of evidence is already in memory and only needs dumping.
Java 14: Flight Recorder as a live data source
JFR event streaming (JEP 349) lets code consume events while the recording runs, in the same process or from outside it, with a design goal of less than 1% overhead. In practice it turns JFR into a source of metrics: GC pauses, lock waits or allocation spikes can feed the monitoring you already have, without an extra agent:
try (var stream = new RecordingStream()) {
stream.enable("jdk.GarbageCollection");
stream.onEvent("jdk.GarbageCollection",
event -> gcPauses.record(event.getDuration())); // e.g. a Micrometer timer
stream.startAsync();
// ... the application keeps running
}
Java 21: answers in the terminal, and visibility into virtual threads
Java 21 added jfr view and jcmd JFR.view, about 70 predefined tables such as hot-methods, gc-pauses, allocation-by-site and contention-by-site, printed in the terminal (JDK 21 release notes). A first diagnosis on a server without a desktop no longer needs Mission Control or a file copied to a laptop.
The same release made virtual threads final, and with them came new diagnostics (JEP 444). JFR records jdk.VirtualThreadPinned, enabled by default with a 20 ms threshold, whenever a virtual thread blocks while pinned to its carrier. Thread dumps can now be written as JSON, and generating them does not pause the application, which matters when a service has a hundred thousand threads:
jcmd <pid> JFR.view hot-methods # on a running JVM, no file needed
jcmd <pid> Thread.dump_to_file -format=json threads.json
Java 25: CPU-time profiling, steadier sampling and exact method timing
Java 25 changed how Flight Recorder samples, in three ways.
CPU-time profiling (JEP 509, experimental, Linux only) samples each thread by the CPU time it consumes rather than at fixed wall-clock intervals. The classic execution sampler shows where threads are; the new event shows what burns the processor, which is the right question when the bill or the CPU throttling is the problem. It can run alongside the old sampler:
java -XX:StartFlightRecording=jdk.CPUTimeSample#enabled=true,filename=profile.jfr -jar app.jar
jfr view cpu-time-hot-methods profile.jfr
Cooperative sampling (JEP 518) walks stacks only at safepoints while minimizing safepoint bias, which makes the sampler more stable without making its results less accurate.
Method timing and tracing (JEP 520) counts the calls and the average time of chosen methods, or records their stack traces, by instrumenting bytecode at runtime. No code change, no agent, and it can be switched on for a running JVM through jcmd. The JEP's own example times every static initializer in an application:
java '-XX:StartFlightRecording:method-timing=::<clinit>,filename=clinit.jfr' -jar app.jar
jfr view method-timing clinit.jfr
It is meant for a handful of methods at a time, such as one suspicious repository call or a slow startup path; for broad questions, sampling stays the right tool.
Java 27: recordings you can share
JFR recordings include the command line, environment variables and system properties, which often hold passwords and API keys. Java 27 redacts them inside the process, before the recording is written (JEP 536). That removes the main reason security teams refuse to let a production recording leave the server.
Why modernizing an old JDK is worth it
Better tools are half of the case. The other half is that the runtime itself got faster and leaner, and you get most of it without changing a line of code. These are the changes we measure first when we move a client's service to a current release:
| Change | Java version | What it gives you |
|---|---|---|
| Generational ZGC (JEP 439) | 21 | Short GC pauses with lower heap overhead and less risk of allocation stalls |
| Virtual threads (JEP 444) | 21 | One thread per request for services that mostly wait on I/O |
| No pinning on synchronized (JEP 491) | 24 | Virtual threads work with older libraries that use synchronized |
| AOT cache (JEP 483) | 24 | Spring PetClinic started 42% faster in the JEP's measurement |
| Compact object headers (JEP 519) | 25 | 22% less heap and 8% less CPU on SPECjbb2015 in one setting; default from Java 27 |
| Faster G1 write barriers (JEP 522) | 26 | 5 to 15% more throughput for code that updates many object references |
| G1 as the default everywhere (JEP 523) | 27 | Small containers no longer fall back to the Serial collector |
Support is the second reason. Oracle's premier support for Java 8 ended in March 2022 and for Java 11 in September 2023; both now run on extended support, and the libraries around them are moving on. Spring Boot 3 and 4 need Java 17 at least, so a Java 8 service is also stuck on a Spring version that no longer receives fixes. The dates for every release are in our Java end-of-life calendar.
The third reason is cost, and here the upgrade often pays for itself. Less heap per object, cheaper garbage collection and faster startup mean smaller instances or fewer of them. An upgrade to Java 25 often removes the problem you were about to profile.
The work is usually smaller than teams fear. We moved an online wine merchant's store and back office from Java 17 to Java 25 in about four weeks, with no feature freeze, and regression tests written beforehand caught two breaks before release (case study). The order that works for us:
- List every service with its JDK, framework versions and dependencies that have no supported release.
- Record a baseline: a JFR recording and a load test on the current version.
- Add tests around the behavior the business depends on before changing anything.
- Upgrade one service at a time, with the framework first where it requires a newer JDK.
- Repeat the recording and the load test, compare, and only then switch on runtime features such as compact object headers or the AOT cache.
The JVM mechanisms behind these gains are explained in how Java enables high performance, and the runtime options worth testing after an upgrade in our guide to improving Java performance.
If a Java system is slow or expensive to run and the profiles do not make the cause obvious, our performance engineering team finds the bottleneck under production-like load and verifies the fix before it ships.