JIT 컴파일러란
- Java와 같은 인터프리터 방식 언어에서 실행 성능을 최적화하는 컴파일 기술
- 일반적으로 Java 프로그램은 바이트코드(.class 파일)로 변환된 후 JVM이 이를 해석하여 실행
- JIT 컴파일러는 자주 실행되는 코드(핫스팟 코드)를 감지하여 네이티브 머신 코드로 변환함으로써 실행 속도를 개선
JIT 컴파일러 동작 방식
- Java 코드 → 바이트코드 변환
- javac 컴파일러가 Java 소스를 바이트코드(.class 파일)로 변환
- JVM 인터프리터 실행
- JVM이 바이트코드를 한 줄씩 해석하며 실행 (초기 실행 속도 느림)
- JIT 컴파일러 개입
- JVM이 **반복 실행되는 코드(핫스팟 코드)**를 감지하여 JIT 컴파일 시작
- 네이티브 코드 변환 후 캐싱
- 감지된 코드를 네이티브 머신 코드로 변환하여 캐시에 저장
- 최적화된 코드 실행
- 이후 동일한 코드 실행 시 네이티브 코드 활용 → 성능 향상
JIT 컴파일러의 주요 최적화 기법
- 인라이닝(Inlining): 작은 메서드를 직접 삽입하여 호출 비용 감소
- 루프 언롤링(Loop Unrolling): 루프 반복 횟수를 줄여 불필요한 연산 제거
- Dead Code 제거: 실행되지 않는 코드 삭제
- Escape Analysis: 객체가 메서드 내부에서만 사용되면 힙 대신 스택에 할당하여 GC 부담 감소
- 가비지 컬렉션 최적화: JIT이 GC 영향을 덜 받도록 최적화
Java 버전별 JIT 컴파일러
| Java 버전 |
JIT 컴파일러 |
특징 |
| Java 1.2 |
HotSpot JIT 도입 |
최초 JIT 지원 |
| Java 1.3 - 1.7 |
HotSpot (C1/C2) |
C1(Client), C2(Server) 도입 |
| Java 8 |
Tiered Compilation |
C1과 C2 조합 최적화 |
| Java 9+ |
Graal JIT (실험적) |
GraalVM 기반 JIT 도입 |
| Java 17+ |
Graal JIT 안정화 |
향상된 최적화 지원 |
JVM 옵션으로 JIT 튜닝하기(튜닝하지 않아도 기본적으로 사용)
# 기본 HotSpot JVM (C1/C2 자동 선택)
java -jar app.jar
# C1 JIT 강제 사용 (빠른 응답, 적은 최적화)
java -XX:+TieredCompilation -XX:+UseC1Compiler -jar app.jar
# C2 JIT 강제 사용 (최적화 성능 중점)
java -XX:+TieredCompilation -XX:+UseC2Compiler -jar app.jar
# Graal JIT 활성화 (Java 9+)
java -XX:+UnlockExperimentalVMOptions -XX:+UseJVMCICompiler -jar app.jar
네이티브 코드가 성능이 빠르면 모두 네이티브로 바꾸면 안되는가?
- 네이티브로 변경은 시간이 비교적 오래 걸림
- 네이티브 변경시 메모리 사용량 증가
- 네이티브는 정적인 코드라 실행중 최적화 불가능
- 따라서, 동적으로 필요한 부분만 네이티브로 변환하는 전략이 효율적
JIT와 웜업 배포
- JIT는 자주 사용되는 코드를 네이티브로 바꾸는 방법을 사용
- 서비스 배포시 처음에는 네이티브 코드가 없게 되어 성능이 느림
- 따라서, 웜업 배포를 통해 점진적으로 트래픽이 증가되면 자주 사용되는 코드는 네이티브로 변환되어 성능 문제를 해결
- https://5jyan.tistory.com/55