javac에 의해 컴파일 단계에서 만들어진 class 파일은 바이트 코드(byte code)로 컴퓨터가 이해할 수 있는 언어는 아닙니다. 따라서 이 바이트 코드를 리눅스, 맥, 윈도우 등에 따라 알맞는 기계어로 변환해야하는데 이 작업을 JIT 컴파일러에서 수행합니다.
프로그램 실행 방식은 인터프리트 방식과 정적 컴파일 방식으로 나눌 수 있습니다. 인터프리트 방식은 프로그램을 실행할 때마다 컴퓨터가 알아 들을 수 있는 언어로 변환하는 작업을 수행합니다. 따라서, 간편하기는 하지만 성능이 매우 느릴 수밖에 없습니다. 정적 컴파일 방식은 실항하기 전에 컴퓨터가 알아 들을 수 있는 언어로 변환하는 작업을 미리 실행합니다. 따라서, 변환 작업을 딱 한번만 수행합니다.
JIT은 Just-In-Time의 약자입니다. JIT 컴파일러는 일반 인터프리트 방식에 비해 빠른 실행 속도를 제공하기 위해 만들어졌습니다. 명칭은 컴파일러이지만 실행시에 적용되는 기술입니다. 자바와 .NET 등에서 사용됩니다.
JIT은 인터프리트와 정적 컴파일을 혼합한 방식을 사용합니다. 변환 작업은 인터프리터에 의해서 지속적으로 수행되지만, 일부 코드의 정보는 캐시 메모리에 담아두었다가 재사용합니다. JVM의 캐시 공간은 매우 작기 때문에 모든 코드들을 캐시하지는 않습니다. 특정 코드가 반복 수행 돼 4 레벨에 도달하는 경우 C2 컴파일러에 의해 바이트 코드를 기계어로 번역하고 이를 캐시에 저장합니다. 그 전 레벨에서는 바이트 코드를 기계어로 번역해 실행하기만 합니다.
- level 0: Interpreter
- level 1: C1-no profiling
- level 2: C1-limited profiling
- level 3: C1-full profiling
- level 4: C2
C1 컴파일러는 JIT 컴파일러의 최적화를 줄이되 빠르게 컴파일되는 컴파일러이며, 이런 특성은 클라이언트 레벨에서 많이 사용됩니다. 클라이언트에서는 앱을 띄울 때 빠르게 뜨는 것이 중요하기 때문입니다. C2 컴파일러는 구동 시간은 느리지만 JIT 컴파일러의 최적화를 많이 진행해 빠른 연산 속도를 보이는 컴파일러입니다. 서버의 경우 한번 구동되고 나면 오랜 기간 서비스하며, 많은 양의 연산을 수행하기 때문에 C2 컴파일러가 더 적합합니다. 그 외에 OpenJDK의 Graal 컴파일러도 있다고 합니다.