본문으로 건너뛰기
안동민 개발노트 아이콘

안동민 개발노트

본문 시작
16장 : JVM과 진단

JFR로 실행 기록 읽기

Java Flight Recorder는 JVM과 애플리케이션의 이벤트를 낮은 오버헤드로 기록합니다.

CPU 샘플, 할당, GC, 락, 파일과 소켓 I/O, 스레드 이벤트를 같은 시간축에서 볼 수 있습니다.

프로파일 한 장만 보고 원인을 확정하기보다 사용자 증상이 나타난 구간과 이벤트를 맞춰야 합니다.

jcmd로 실행 중 기록을 시작하거나 -XX:StartFlightRecording으로 시작 시점부터 남길 수 있습니다.

이 절에서는 기록, 구간 선택, 가설 검증 순서를 다룹니다.

JFR 이벤트에는 정확한 발생 이벤트와 일정 주기로 관찰한 샘플이 함께 있습니다. CPU 샘플에서 메서드 비율이 높다는 것은 그 메서드가 정확히 그만큼 시간을 썼다는 확정값이 아니라, 표본에서 자주 관찰됐다는 증거입니다. 설정과 임계값을 함께 읽어야 과도한 결론을 피할 수 있습니다.


녹화 목적과 기간을 먼저 정한다

지연 급증, 높은 CPU, 많은 할당처럼 질문을 정하고 문제가 포함될 시간 범위를 확보합니다.

필요 이상으로 상세한 이벤트를 오래 켜지 않습니다.


jcmd로 실행 중인 JVM을 제어한다

jcmd에서 PID를 찾고 JFR.start, JFR.dump, JFR.stop으로 녹화를 관리합니다.

대상 JVM을 실행한 사용자 권한이 필요합니다.

예제는 외부 도구 없이 프로그램 안에서 Recording을 열고 사용자 정의 이벤트 하나를 기록합니다. begincommit 사이가 이벤트의 지속 시간이고 items는 나중에 필터링할 구조화된 필드입니다.

FlightRecording.java
import java.nio.file.Files;
import jdk.jfr.Event;
import jdk.jfr.Label;
import jdk.jfr.Name;
import jdk.jfr.Recording;

public class FlightRecording {
    @Name("demo.Work") @Label("Demo Work")
    static final class WorkEvent extends Event {
        @Label("Items") int items;
    }

    public static void main(String[] args) throws Exception {
        var file = Files.createTempFile("java-tutorial-", ".jfr");
        try (var recording = new Recording()) {
            recording.enable(WorkEvent.class);
            recording.start();
            var event = new WorkEvent();
            event.begin();
            event.items = 25;
            event.commit();
            recording.stop();
            recording.dump(file);
        }
        System.out.println(Files.size(file) > 0);
        Files.delete(file);
    }
}

true는 파일에 기록 데이터가 생겼다는 최소 확인입니다. 성능 원인을 알려 주는 결과는 아니며, 실제 조사에서는 생성한 jfr 파일을 JMC나 jfr 명령으로 열어 이벤트 시간과 필드를 읽어야 합니다.

true

프로그램이 사용자 Work 이벤트를 기록해 실제 JFR 파일을 만들었는지 크기로 확인합니다.


JMC에서 같은 시간 구간의 이벤트를 겹쳐 본다

CPU hot method와 GC pause, monitor block, I/O 대기가 같은 순간에 일어났는지 비교합니다.

명령에서 duration=60s는 문제 구간을 포함할 관찰 창을 정하고, dump는 실행 중인 기록의 현재 내용을 별도 파일로 보존합니다. 기록 이름을 붙이면 여러 녹화를 혼동하지 않고 제어할 수 있습니다.

비교 1
jcmd
jcmd <pid> JFR.start name=diagnosis settings=profile duration=60s filename=app.jfr
jcmd <pid> JFR.dump name=diagnosis filename=snapshot.jfr
jcmd <pid> JFR.stop name=diagnosis
비교 2
java -XX:StartFlightRecording=filename=startup.jfr,duration=60s App
비교 3
// JMC에서 증상이 나타난 시간 구간의 CPU, allocation, GC, lock, I/O를 함께 본다.

기록은 원인이 아니라 증거다

샘플 비율과 이벤트 임계값을 이해하고 코드 경로와 재현 입력을 대조합니다.

한 번의 기록으로 일반화하지 않습니다.

WorkEvent를 반복문에서 여러 번 commit하고 items 값을 1부터 늘려 보세요. JMC에서 demo.Work만 필터링해 개수와 지속 시간을 확인한 뒤, 같은 시간대의 CPU·할당 이벤트와 겹쳐 봅니다. 사용자 이벤트는 업무 구간과 JVM 증거를 연결하는 표식으로 유용합니다.

운영 녹화에서는 먼저 낮은 오버헤드 설정으로 증상 구간을 확보하고, 가설이 좁혀졌을 때 필요한 이벤트만 상세하게 켭니다. 문제 발생 뒤 상세 기록을 시작하면 직전 원인이 사라질 수 있으므로 순환 녹화와 보관 정책도 함께 설계합니다.


JFR은 느리다는 느낌을 CPU, 할당, GC, 락, I/O 이벤트로 바꿔 검증 가능한 성능 가설을 만듭니다.