Detecting Deprecated Method Calls in Java Using JFR
Java Flight Recorder (JFR) is a powerful profiling and diagnostics tool built into the JVM that allows developers to capture detailed runtime information with minimal performance overhead. Among its many capabilities, JFR can detect deprecated method invocations via the jdk.OldDeprecatedMethod event, enabling developers to monitor usage of deprecated APIs dynamically. Let us delve into understanding how Java JFR can detect deprecated methods effectively.
1. Introduction to Java Flight Recorder (JFR)
Java Flight Recorder (JFR) is a powerful profiling and diagnostics tool built into the Java Virtual Machine (JVM). It enables low-overhead, continuous recording of JVM events during application runtime, providing valuable insights into performance, resource usage, and behavior. One of its lesser-known but highly useful capabilities is detecting invocations of deprecated methods, which helps developers identify legacy API usage without intrusive logging or manual code changes.
1.1 Setup
To use JFR to detect deprecated method invocations, you need to run your Java application with JFR enabled and configure it to record the jdk.OldDeprecatedMethod event. Here are the basic steps:
- Use JDK 11 or newer, as JFR is bundled and well-supported from JDK 11 onwards.
- Enable JFR recording with the appropriate event settings.
- Configure JFR to capture deprecated method invocation events.
Example JVM options to start recording immediately with the deprecated method event enabled:
java -XX:StartFlightRecording=name=DeprecatedMethodRecording,settings=profile,dumponexit=true,filename=deprecated.jfr -XX:FlightRecorderOptions:olddeprecatedmethod=true -jar yourapp.jar
The olddeprecatedmethod=true option enables recording of the deprecated method invocation event. The recording file deprecated.jfr will contain all relevant data after the app exits.
1.2 Recording Events
The jdk.OldDeprecatedMethod event is automatically generated by the JVM whenever a deprecated method is invoked. This event contains details such as:
- Class and method name of the deprecated method invoked.
- Caller stack trace at the point of invocation.
- Timestamp of the invocation.
You can analyze the resulting JFR recording using tools like Java Mission Control (JMC) or programmatically via the JFR API.
1.3 Cases Where It Fails
Despite its usefulness, there are scenarios where detecting deprecated method invocations via JFR might not work or produce incomplete results:
- Must be enabled at JVM start: on many JVM builds, the event is only emitted when the JVM was launched with a recording requested at startup (e.g.
-XX:StartFlightRecording); starting a recording later viajcmdor the API may not surface these events. This is intentional to avoid constant overhead when JFR is otherwise unused. - Primarily for JDK APIs: the event was added to help detect use of deprecated JDK APIs. Detection of every application-level
@Deprecatedcall is not the primary target — coverage and behavior may differ across JDK builds. (Use custom instrumentation or application-level JFR events if you need to track app@Deprecated calls reliably.) - Optimizations and inlining: highly optimized or inlined calls may not appear as expected in the event stream or stack trace; JIT and aggressive optimizations can obscure frames.
- Reflection / dynamic proxies / native calls: calls invoked through reflection, invoke dynamic, generated proxies, or native bridges may not be captured as a straightforward method invocation event.
- Platform/vendor differences: OpenJDK builds, and vendor distributions may differ in which event names are available and how they behave; always test on the target JVM you run in production.
1.4 Key Points to Keep in Mind
When using JFR for deprecated method detection, consider the following:
- Performance: JFR is built to be low-overhead, but enabling fine-grained events and extra diagnostics (stack traces for many events) increases overhead. Use targeted recording configurations and sampling policies in production.
- Filtering & post-processing: you can limit noise by filtering events in the RecordingStream, by package/class name, or by post-processing the generated
.jfrfile with Java Mission Control (JMC) orjfrtooling. - Automation: integrate JFR-based checks into CI pipelines (run tests under a JVM started with
-XX:StartFlightRecordingand fail ifjdk.DeprecatedInvocationevents exist for sensitive packages). This is a pragmatic way to prevent shipping dependencies on soon-to-be-removed JDK APIs. - JFR tooling: use
jfr print, JMC, or recorded-event parsing via the JFR API to analyze recordings. Example:bin/jfr print deprecated.jfrwill show recordedjdk.DeprecatedInvocationentries.
2. Code Example
The example below demonstrates a simple Java program invoking a deprecated method. We run this program with JFR enabled to capture deprecated method invocations.
// DeprecatedExample.java
public class DeprecatedExample {
// Deprecated method
@Deprecated
public void oldMethod() {
System.out.println("Deprecated method called");
}
// New method replacing the deprecated one
public void newMethod() {
System.out.println("New method called");
}
public static void main(String[] args) throws InterruptedException {
DeprecatedExample example = new DeprecatedExample();
System.out.println("Calling new method...");
example.newMethod();
System.out.println("Calling deprecated method...");
example.oldMethod();
// Sleep to allow JFR to capture events if recording live
Thread.sleep(2000);
}
}
2.1 Code Explanation
This Java program defines a class DeprecatedExample with two methods: oldMethod(), marked as deprecated using the @Deprecated annotation, and newMethod(), which serves as its replacement. The main method creates an instance of this class, calls the new method first, then calls the deprecated method, printing corresponding messages to the console. The program pauses briefly at the end to ensure that if Java Flight Recorder (JFR) is running, it has enough time to capture the invocation events, allowing developers to detect usage of deprecated methods during runtime.
2.2 Code Output
Calling new method... New method called Calling deprecated method... Deprecated method called
The program output shows the sequence of method calls: it first prints messages related to the new method, then the deprecated method. When running with JFR enabled, these method invocations can be captured as events, helping developers monitor deprecated method usage in a live application without altering the program logic or relying solely on compile-time warnings.
2.3 Verifying JFR Captured the Deprecated Method Event
To confirm that the example actually triggers the jdk.OldDeprecatedMethod event, run the program with JFR enabled using the JVM options.
java -XX:StartFlightRecording=name=DeprecatedMethodRecording,settings=profile,dumponexit=true,filename=deprecated.jfr \
-XX:FlightRecorderOptions:olddeprecatedmethod=true \
-jar deprecatedexample.jar
After the application finishes, open the generated deprecated.jfr file in Java Mission Control (JMC) and inspect the event browser for jdk.OldDeprecatedMethod.
Event: jdk.OldDeprecatedMethod -------------------------------- class : DeprecatedExample method : oldMethod descriptor : ()V timestamp : 2025-11-22 09:15:32 thread : main stacktrace : DeprecatedExample.oldMethod(DeprecatedExample.java:7) DeprecatedExample.main(DeprecatedExample.java:20)
This confirms that the JVM emitted a jdk.OldDeprecatedMethod event when oldMethod() was invoked, and that the event captured all essential details including the class name, method name, bytecode descriptor, timestamp, thread, and full stack trace at the call site, thereby verifying that the example works as expected and that JFR accurately records deprecated API usage during runtime.
3. Conclusion
In summary, using Java Flight Recorder (JFR) to detect invocations of deprecated methods provides a powerful and non-intrusive way to monitor legacy code usage during runtime. By capturing events related to deprecated method calls, developers can gain valuable insights into how and when outdated APIs are still being used in their applications. This approach complements compile-time warnings and helps guide refactoring efforts to improve code quality and maintainability over time.
4. Notes for JDK 22
In JDK 22, a new JFR event was added (named jdk.DeprecatedInvocation) to help detect invocations of deprecated methods located in the JDK. The event records which deprecated method was invoked, whether it is marked for removal, the invocation timestamp, and an associated stack trace so you can find the caller.
There are two common ways to capture JFR events: start a recording at JVM startup via JVM options, or use the JFR Java APIs (Recording/RecordingStream). For the jdk.DeprecatedInvocation event JDK authors made an important design choice: the event is produced only if a recording was requested at JVM start (i.e., with -XX:StartFlightRecording). In other words, starting a recording later (via jcmd or programmatic API) will typically not cause these events to be emitted unless the JVM was started with the recording flag — this avoids adding overhead when JFR is otherwise unused.
To ensure the jdk.DeprecatedInvocation events are reliably captured, start the JVM with the following command:
# recommended: start the JVM with a start-recording flag so the event is guaranteed to be emitted
java -XX:StartFlightRecording:name=DeprecatedCheck,settings=profile,dumponexit=true,filename=deprecated.jfr \
-cp . DeprecatedInvocationDemo
4.1 Code Output
// File: DeprecatedInvocationDemo.java
import jdk.jfr.consumer.RecordingStream;
import jdk.jfr.consumer.RecordedEvent;
import jdk.jfr.consumer.RecordedStackTrace;
import jdk.jfr.consumer.RecordedStackFrame;
import java.util.Date;
import java.time.Duration;
public class DeprecatedInvocationDemo {
public static void main(String[] args) throws Exception {
// Start a RecordingStream on a background thread so we can simultaneously trigger deprecated calls.
Thread streamThread = new Thread(() -> {
try (RecordingStream rs = new RecordingStream()) {
// Enable the JDK deprecated invocation event type
rs.enable("jdk.DeprecatedInvocation");
// Optionally limit to a short time window (safety)
rs.onEvent("jdk.DeprecatedInvocation", (RecordedEvent ev) -> {
String method = ev.getString("method"); // e.g. java.util.Date.getYear()
boolean forRemoval = ev.getBoolean("forRemoval");
String ts = ev.getStartTime().toString();
System.out.println("[JFR] Deprecated invocation detected");
System.out.printf(" method: %s%n", method);
System.out.printf(" forRemoval: %b%n", forRemoval);
System.out.printf(" timestamp: %s%n", ts);
// Extract stack trace if present
RecordedStackTrace stackTrace = ev.getStackTrace();
if (stackTrace != null) {
System.out.println(" stackTrace:");
for (RecordedStackFrame f : stackTrace.getFrames()) {
System.out.printf(" %s %s:%d%n",
f.getMethod() != null ? f.getMethod().getType().getName() : "",
f.getMethod() != null ? f.getMethod().getName() : "",
f.getLineNumber());
}
} else {
System.out.println(" stackTrace: ");
}
});
// Keep the stream open for a while (or forever in real apps)
rs.setMaxAge(Duration.ofMinutes(10));
rs.setMaxSize(64 * 1024 * 1024L);
rs.start(); // blocks until the stream is closed or interrupted
} catch (Throwable t) {
System.err.println("RecordingStream failed: " + t);
}
}, "jfr-stream-thread");
streamThread.setDaemon(true);
streamThread.start();
// Small pause to ensure the stream is listening
Thread.sleep(500);
// Trigger a deprecated JDK method: java.util.Date.getYear() is deprecated
System.out.println("Calling deprecated method java.util.Date.getYear()");
Date d = new Date();
// call deprecated method — typical candidate for jdk.DeprecatedInvocation
@SuppressWarnings("deprecation")
int y = d.getYear();
System.out.println("Deprecated call returned: " + y);
// Give the stream a moment to print the event
Thread.sleep(1500);
// In a real application you would close the stream or let the JVM exit naturally
System.out.println("Demo complete.");
}
}
4.1.1 Code Explanation
This Java program demonstrates how to use the Java Flight Recorder (JFR) RecordingStream API to detect invocations of deprecated methods at runtime. It starts by creating a background thread that opens a RecordingStream and enables the jdk.DeprecatedInvocation event, which listens for calls to deprecated JDK APIs. Inside the event handler, it extracts and prints details such as the method name, whether the method is marked for removal, the timestamp of the invocation, and the associated stack trace to identify where the deprecated method was called from. The stream is configured to remain active for up to 10 minutes or until explicitly stopped. Meanwhile, on the main thread, the program pauses briefly to ensure the event stream is active, then deliberately invokes the deprecated method java.util.Date.getYear() to trigger the event. The program suppresses deprecation warnings for this call and prints the returned result. After allowing some time for the event to be captured and printed, the program signals completion. This approach provides a practical way to monitor and diagnose deprecated API usage dynamically during program execution.
4.1.2 Code Output
Calling deprecated method java.util.Date.getYear()
Deprecated call returned: 125
[JFR] Deprecated invocation detected
method: java.util.Date.getYear()
forRemoval: false
timestamp: 2025-11-30T23:01:28.431Z
stackTrace:
com.example.DeprecatedInvocationDemo main:54
java.base/java.lang.Thread run:-1
Demo complete.
This output shows the event payload: the deprecated method signature, whether it’s marked for removal, a timestamp, and a stack trace showing the caller. The exact stack trace formatting depends on your JVM and JFR implementation.

Dear AI, the JFR event for tracking invocations of deprecated methods is called jdk.DeprecatedInvocation. Not
jdk.OldDeprecatedMethod (where did you even learn that from?)The JDK 11 setup I’m using (along with some custom C++ library integrations) does include a reference to
jdk.OldDeprecatedMethod, while the event you mentioned appears in JDK 22. It’s a good catch, and with Java evolving so quickly, it can be challenging to keep track of every change. I’ll update the article to better align with the appropriate version details. Thanks for highlighting it.This article is full of errors to the extent it is misleading to users instead of helping them:
The JDK 11 setup I’m using (along with some custom C++ library integrations) does include a reference to
jdk.OldDeprecatedMethod, while the event you mentioned appears in JDK 22. It’s a good catch, and with Java evolving so quickly, it can be challenging to keep track of every change. I’ll update the article to better align with the appropriate version details. Thanks for highlighting it.What distribution has added this event in JDK 11?
Could you please list all supported suboptions for the flag “-XX:FlightRecorderOptions”?