Jan @Novoj Novotný
# Start & finish with JVM
java -jar my-application.jar \
-XX:StartFlightRecording=name=my-application_recording,dumponexit=true,filename=myrecording.jfr
or
# retrieve Java PID
$ jps
# use jcmd to start recording for particular PID
$ jcmd 1 JFR.start duration=10s filename=recording.jfr
# use jcmd to dump current results of recording
$ jcmd 1 JFR.dump filename=recording.jfr
# use jcmd to stop recording
$ jcmd 1 JFR.stop
Support for multiple recordings at a time!
# prints summary of recording (how many events, duration, allocation sizes, etc.)
$ jfr summary recording.jfr
# prints all events in recording
$ jfr print recording.jfr
# prints filtered events in recording
$ jfr print recording.jfr --event jdk.GarbageCollection
# exports events to CSV
$ jfr print recording.jfr --event jdk.GarbageCollection --format csv
# concatenates multiple recordings into single file
$ jfr assemble /path concatenated-file.jfr
# divide large recording into smaller chunks
$ jfr disassemble --max-chunks 5 /path/to/recording.jfr
But you should probably want to use Java Mission Control or such tool.
public class JfrTest {
public static void main(String[] args) throws InterruptedException {
// Create a new recording
try (Recording recording = new Recording()) {
// Limit memory allocations: only capture allocations larger than 1MB (1048576 bytes)
recording.setSettings(Map.of(
"jdk.ObjectAllocationInNewTLAB#threshold", "1 ms", // Only record events if they exceed 1ms
"jdk.ObjectAllocationOutsideTLAB#threshold", "1 ms", // Threshold for non-TLAB allocations
"jdk.GarbageCollection#threshold", "10 ms", // Record GC events if GC pause exceeds 10ms
"jdk.CPULoad#period", "500 ms", // Set sampling period to 500ms for CPU load
"jdk.ThreadSleep#threshold", "1 ms" // Record if thread sleep exceeds 1ms
));
// Start the recording
recording.start();
// Keep the recording running for some time, then stop it
Thread.sleep(10000); // Simulate workload for 10 seconds
recording.stop();
}
}
}
public class JfrTest {
public static void main(String[] args) {
try (RecordingStream stream = new RecordingStream()) {
// Set a sampling interval for CPU load events
stream.enable("jdk.CPULoad").withPeriod(Duration.of(500, ChronoUnit.MILLIS));
// Set a threshold for memory allocations (only large allocations)
stream.enable("jdk.ObjectAllocationInNewTLAB").withThreshold(Duration.of(1, ChronoUnit.MILLIS));
stream.enable("jdk.ObjectAllocationOutsideTLAB").withThreshold(Duration.of(1, ChronoUnit.MILLIS));
// Handle the event in real-time
stream.onEvent(event -> {
System.out.println(event.getEventType().getName() + ": " + event);
});
// Start the stream
stream.start();
}
}
}
@Name("io.evitadb.system.BackgroundTaskTimedOut")
@EventGroup(
value = "io.evitadb.system",
name = "evitaDB - System",
description = "evitaDB events related to system-wide operations such as tasks, threads, etc."
)
@Category({"evitaDB", "System"})
@Description("Event that is raised when a background task has timed out and has been canceled.")
@Label("Background task timed out")
public class BackgroundTaskTimedOutEvent extends AbstractSystemEvent {
/**
* Number of timed out tasks.
*/
@Label("Timed out tasks")
@Description("Number of timed out and canceled tasks.")
@ExportMetric(metricType = MetricType.COUNTER)
private final int timedOutTasks;
/**
* The name of the background task.
*/
@Label("Task name")
@Description("Name of the background task.")
@ExportMetricLabel
final String taskName;
public BackgroundTaskTimedOutEvent(@Nonnull String taskName, int timedOutTasks) {
this.taskName = taskName;
this.timedOutTasks = timedOutTasks;
}
}
@Category("MyEvents")
@Label("Cache Stats")
@Description("Simple cache statistics")
@Period("10s")
@StackTrace(false)
public class CacheStatsEvent extends Event {
@Label("Cache Name")
public String cacheName;
@Label("Cache Size")
public int cacheSize;
public static void enableStatsRecording(String cacheName, Map, ?> cache) {
final WeakReference
@Label("CustomEvent")
public class CustomEvent extends Event {
@Label("Message")
private String message;
@SettingDefinition
public static MessageLengthThresholdControl messageLengthThreshold
= new MessageLengthThresholdControl();
public CustomEvent(String message) {
this.message = message;
}
public static void recordEvent(String message) {
// Use the threshold in the logic
if (message != null && message.length() > messageLengthThreshold.getValue()) {
CustomEvent event = new CustomEvent(message);
if (event.isEnabled()) {
event.commit();
}
}
}
}
public class MessageLengthThresholdControl extends SettingControl {
private static int threshold = 10; // Default threshold
@Override
public Integer combine(Integer currentValue, Integer newValue) {
return newValue; // Replace the current value with the new one
}
@Override
public Integer parse(String value) {
return Integer.parseInt(value); // Parse the string setting into an integer
}
@Override
public Integer getValue() {
return threshold; // Get the current threshold
}
@Override
public void setValue(Integer value) {
threshold = value; // Update the threshold dynamically
}
}
Using `jfr configure`:
# sets messageLengthThreshold to 20 for CustomEvent in Java process with PID 12345
$ jcmd 12345 JFR.configure jdk.jfr.CustomEvent#messageLengthThreshold=20
# starts recording with custom profile
java -XX:StartFlightRecording:filename=recording.jfr,settings=path/to/custom_profile.jfc -cp yourapp.jar com.example.Main
With particular JFC configuration:
Contact me @Novoj or novotnaci@gmail.com