개발 학습 실습실

이 사이트의 실습은 자바스크립트를 켜야 움직입니다. 아래는 같은 내용을 글로 적은 것입니다.

공부 › Java › JVM

JIT 워밍업

배포 직후의 첫 요청들은 왜 느린가

자바 코드는 바이트코드로 컴파일되어 JVM이 한 명령씩 해석하며 실행합니다. JVM은 자주 실행되는 메서드를 찾아내 기계어로 다시 컴파일하고(JIT), 그 뒤로는 훨씬 빨라집니다.

실행으로 대조 · JDK 21에서 실제로 잰 값

이 실습에 나오는 개념

JIT 컴파일러
프로그램 실행 중에 자주 실행되는 바이트코드를 기계어로 컴파일해 두고 이후에는 그 기계어를 직접 실행하는 JVM의 구성 요소다. 처음에는 인터프리터로 바이트코드 명령을 하나씩 해석해 실행하다가, 반복 실행되는 부분을 찾아 최적화한다. 그래서 자바 애플리케이션은 기동 직후보다 어느 정도 실행된 뒤에 더 빨라진다(워밍업).
바이트코드
자바 컴파일러(javac)가 소스 코드를 변환해 만든 중간 형태의 코드로 .class 파일에 담긴다. 특정 CPU나 운영체제의 기계어가 아니라 JVM이 이해하는 명령어라서 어느 플랫폼의 JVM에서든 실행된다.
JVM(자바 가상 머신)
자바 바이트코드를 읽어 실행하는 프로그램이다. 운영체제마다 그에 맞는 JVM이 있어서, 한 번 컴파일한 자바 프로그램을 수정 없이 여러 운영체제에서 실행할 수 있다. 클래스를 읽어 들이고, 메모리를 관리하고(가비지 컬렉션), 바이트코드를 기계어로 바꿔 실행하는 일을 맡는다.
클래스 로더
클래스 파일을 찾아 메모리로 읽어 들이는 JVM의 구성 요소다. 클래스는 프로그램 시작 때 전부 읽는 것이 아니라 처음 필요해지는 시점에 로딩된다. 읽어 들인 클래스는 JVM이 링킹(검증, 준비, 그리고 심볼릭 참조의 해석)과 초기화(static 초기화 실행)를 거쳐 사용할 수 있게 만든다.

이렇게 바꿔 보세요

처음 설정으로 실행하면

  1. 자바 컴파일러(javac)는 소스를 기계어가 아니라 바이트코드로 바꿉니다. 프로그램이 시작되면 JVM의 인터프리터가 이 바이트코드를 한 명령씩 해석해 실행합니다. 어떤 CPU에서든 돌아가지만, 기계어를 직접 실행하는 것보다 훨씬 느립니다.
  2. 1번째 묶음: 5.8ms. 처음에는 인터프리터로 실행됩니다. JVM은 메서드가 몇 번 불렸는지, 반복문이 몇 번 돌았는지를 세고 있다가, 기준을 넘자 이 메서드를 기계어로 컴파일합니다. 이 묶음은 컴파일된 코드가 준비되기 전까지 해석 실행으로 돌았습니다(컴파일은 다른 스레드에서 진행).
  3. 2번째 묶음: 1.7ms. 컴파일된 기계어로 실행되기 시작했습니다. 첫 묶음보다 3~4배 빠릅니다. JVM은 실행하면서 모은 정보(어느 분기가 자주 타는지 등)로 한 번 더 강하게 최적화합니다.
  4. 3번째 묶음: 1.4ms.
  5. 4번째 묶음: 1.7ms.
  6. 5번째 묶음: 1.5ms.
  7. 6번째 묶음: 1.4ms.
  8. 7번째 묶음: 1.5ms.
  9. 8번째 묶음: 1.4ms.
  10. 9번째 묶음: 1.4ms.
  11. 10번째 묶음: 1.4ms.
  12. 11번째 묶음: 1.6ms.
  13. 12번째 묶음: 1.7ms.

실행 결과

코드

static long work(int n) {                  // 측정한 메서드
    long s = 0;
    for (int i = 0; i < n; i++) s += (i * 31L) ^ (s >>> 3);
    return s;
}

for (int batch = 1; batch <= 12; batch++) {
    long t = System.nanoTime();
    for (int k = 0; k < 2000; k++) work(1000);          // 묶음 하나 = 2,000번 호출
    print((System.nanoTime() - t) / 1000 + "마이크로초");
}

// 실행: java JitWarmup.java

이 모형의 전제

숫자는 이 PC의 JDK 21에서 위 코드를 실제로 실행해 잰 값입니다(기본 설정 두 번 실행 중 첫 번째, -Xint 한 번). "미리 워밍업"의 값은 기본 설정 측정값에서 첫 묶음을 빼고 이어 붙인 것이고, 마지막 값 하나(1.4ms)는 측정값이 아니라 비슷한 값을 덧붙인 것입니다. 수치는 CPU와 실행 시점에 따라 달라지지만, 첫 구간이 느리고 그 뒤로 빨라지는 모양은 같습니다. 기본 설정의 JVM은 가벼운 컴파일(C1)과 강한 최적화 컴파일(C2)을 단계적으로 적용합니다.

앞 실습: GC와 메모리 누수 · 다음 실습: 클래스 로딩과 초기화

이 실습에 나오는 개념

이 실습에서 보는 것

설정

코드


      
    

그림

실행 비교

이렇게 바꿔 보세요