JavaSeniorTechnical
Что такое JFR и как использовать его для диагностики production performance?
JFR — встроенный в JVM профайлер с оверхедом менее 1%, пригодный для production. Запускается через флаг -XX:StartFlightRecording или jcmd на живом процессе; записывает GC, блокировки, I/O, аллокации, исключения. Анализируется через JMC GUI или jfr CLI.
Java Flight Recorder (JFR)
JFR — встроенный в JVM низкооверхедный профайлер и инструмент трассировки, доступный с Java 11 без лицензионных ограничений. Он записывает события JVM (GC, компиляция JIT, блокировки, аллокации, I/O, исключения, сетевые вызовы) в бинарный файл .jfr с накладными расходами менее 1% CPU — пригоден для production.
Запуск записи
Вариант 1: при старте JVM
java \
-XX:StartFlightRecording=duration=60s,filename=/tmp/app.jfr,settings=profile \
-jar app.jar
Вариант 2: на живом процессе через jcmd
# Найти PID
jps -l
# Начать запись
jcmd <PID> JFR.start duration=120s filename=/tmp/app.jfr settings=profile name=diag
# Проверить статус
jcmd <PID> JFR.check
# Остановить досрочно и сбросить файл
jcmd <PID> JFR.stop name=diag
# Сбросить файл без остановки (dump snapshot)
jcmd <PID> JFR.dump name=diag filename=/tmp/snapshot.jfr
Настройки профилей
settings=default— минимальный оверхед (~0.1%), базовые события.settings=profile— расширенный набор (аллокации, блокировки, I/O), оверхед ~1%.- Кастомные профили: XML-файлы в
$JAVA_HOME/lib/jfr/, редактируются через JMC Mission Control.
Анализ записи
# Вывести все события определённого типа
jfr print --events jdk.GarbageCollection app.jfr
# Фильтр по времени
jfr print --events jdk.SocketRead --time "2025-01-15T10:00:00Z/PT5M" app.jfr
# Краткая сводка
jfr summary app.jfr
# Список всех типов событий в файле
jfr metadata app.jfr | grep "^Event"
Анализ через JDK Mission Control (JMC)
JMC — GUI для JFR, скачивается отдельно с adoptium.net/jmc. Ключевые вкладки:
- Method Profiling — горячие методы (CPU sampling), как flame graph.
- Memory — TLAB аллокации, GC паузы, утечки.
- Threads — блокировки, deadlock detection.
- I/O — медленные файловые и сетевые операции.
- Exceptions — частые исключения (даже пойманные).
Программный доступ через JFR API
import jdk.jfr.*;
import jdk.jfr.consumer.*;
import java.nio.file.Path;
import java.time.Duration;
// Создать кастомное событие
@Label("Database Query")
@Description("Tracks slow SQL queries")
@Category("Application")
@StackTrace(false)
public class DbQueryEvent extends Event {
@Label("SQL")
public String sql;
@Label("Duration ms")
public long durationMs;
}
// Использование
void executeQuery(String sql) {
DbQueryEvent event = new DbQueryEvent();
event.begin();
try {
// execute sql...
} finally {
event.sql = sql;
event.durationMs = event.getDuration().toMillis();
event.commit();
}
}
// Чтение .jfr файла программно
void analyzeRecording(Path jfrFile) throws Exception {
try (RecordingFile rf = new RecordingFile(jfrFile)) {
while (rf.hasMoreEvents()) {
RecordedEvent event = rf.readEvent();
if (event.getEventType().getName().equals("jdk.GarbageCollection")) {
Duration pause = event.getDuration();
System.out.println("GC pause: " + pause.toMillis() + "ms");
}
}
}
}
Диагностика типичных production-проблем
- CPU spike:
jfr print --events jdk.ExecutionSample app.jfr→ найти горячие методы; сравнить frame distribution. - Утечка памяти: события
jdk.ObjectAllocationInNewTLAB+jdk.OldObjectSample— найти аллоцирующий код и объекты, доживающие до old gen. - Медленные запросы:
jdk.SocketRead,jdk.SocketWrite,jdk.FileRead— фильтровать по duration > threshold. - Lock contention:
jdk.JavaMonitorWait,jdk.JavaMonitorEnter— найти монитор с наибольшим суммарным временем ожидания.
Подводные камни
- Размер файла не ограничен по умолчанию. При длинных записях с
settings=profileфайл может занять несколько GB; используйтеmaxsize=500MBилиmaxage=1h. - Оверхед не нулевой при profile. ~1% CPU и дополнительное давление на GC от буферов событий — допустимо для production, но учитывайте при нагрузочном тестировании.
- jcmd требует того же пользователя или root. Команда
jcmd <PID> JFR.startот другого пользователя упадёт с Permission denied — убедитесь, что запускаете от имени пользователя JVM. - Кастомные события теряются без регистрации. Если класс-событие выгружен (classloader leak), события могут не записаться; всегда регистрируйте через
FlightRecorder.register(DbQueryEvent.class). - Временные метки в UTC. JFR пишет время в UTC; при сравнении с логами приложения убедитесь, что временные зоны совпадают.
- Не все методы видны в Method Profiling. JIT-компиляция может inline-ить методы, убирая их из стека; компилируйте с
-XX:+PreserveFramePointerдля точных стеков. - JMC не входит в JDK. Нужно скачивать отдельно; в CI-среде используйте CLI (
jfr print) или библиотекуjdk.jfr.consumer.
Common mistakes
- Отвечать определением без production-сценария.
- Не называть runtime boundary, security boundary или failure mode.
- Игнорировать версию API, observability и тестовую проверку.
What the interviewer is testing
- Объясняет механизм своими словами и без выдуманных API.
- Называет реальные риски, диагностику и критерий корректности.
- Связывает ответ с текущей документацией и миграционными ограничениями.