JIT(Just In Time) 컴파일러는 런타임 시 byte code → machine code로 컴파일하여 java 애플리케이션의 성능을 향상 시키는 컴파일러다.
java는 알다시피 다양한 운영체제에서 실행 될 수 있느도록 플랫폼 중립적 바이트 코드를 포함된 클래스로 구성되어 있다. 런타임에서 jvm은 class 파일을 로드하고 각 바이트 코드의 시맨틱을 판별하여 해당 계산을 수행한다. 이때 JIT 컴파일러는 런타임에서 byte 코드를 원시 시스템 코드로 컴파일하여 java 애플리케이션의 성능을 향상 시킨다.
JIT 컴파일러의 기본 설정은 사용으로 되어 있다. 또한 컴파일을 할 때는 cpu와 메모리가 사용되어 JVM이 처음 시작될 때 수천 개의 메서드가 호출된다. 이러한 메서드를 모두 호출하는 것은 최대 성능에 도달하는 경우에도 시작 시간에 영향을 줄 수 있다.
JIT는 여러 메커니즘을 통해 메서드를 더 높은 최적화 레벨로 다시 컴파일 할 수 있다. JIT 컴파일러는 주기적으로 다시 시작하고 스택의 맨 위에 표시되는 java 메서드를 판별하는 전용 샘플링 쓰레드를 유지보수 한다. 상위 메서드는 성능에 중요한 영향을 주는 것으로 판단되어 상위 레벨로 다시 최적화되는 후보가 된다.
JIT 컴파일러 코드 최적화 방법
select compiler method → JVM이 JIT에 바이트 코드 공급 → JIT에서 바이트 코드의 시맨틱과 구문을 이해
시맨틱 : 바이트 코드의 구조가 jvm이 빠르게 알 수 있도록 해주는 정보, 바이트 코드로 부터 컴파일 체계를 정의하기 위한 기초 역할
JIT 컴파일러는 복수의 쓰레드를 사용하여 컴파일을 수행하여 시작 시간의 성능을 개선한다. 기본적인 컴파일 쓰레드 숫자는 JVM에서 식별되고 시스템 구성에 따라서 결정된다.
쓰레드 수가 최적이 아니면 -XcompilationThreads 옵션을 통해서 JVM 결정을 대체할 수 없다 (아직 사용해본 적 없음)
Level 1 - 인라이닝
소형 메서드 트리가 해당 호출자 트리로 병합되거나 인라인되는 프로세스. 자주 실행되는 메서드 호출 속도가 향상된다. (단순 인라이닝, 호출 그래프 인라이닝, 후미 순환 제거, 가상 호출 보호 최적화)
Level 2 - 로컬 최적화
한 번에 하나의 작은 코드 섹션을 분석하고 개선한다. (로컬 데이터 플로우 분석 및 최적화, 레지스터 사용 최적화, java 관용 표현 단순화)
Level 3 - 제어 플로우 최적화
메서드 내부의 제어 플로우를 분석하고 코드 경로를 재배열하여 효율성을 향상 시킨다.
최적화 되는 내용
- 코드 재정렬, 분할 및 제거
- 루프 감소 및 반전
- 루프 진행 속도 조정 및 루프 고정 코드 동작
- 루프 롤 해제 및 분리
- 루프 버전화 및 특수화
- 예외 지시 최적화
- 전환 분석
Level 4 - 글로벌 최적화
전체 메서드에서 동시에 작동한다. 더 많은 컴파일 시간(많은 비용)이 필요한 작업이지만 성능을 크게 향상시킬 수 있다.
- 글로벌 데이터 플로우 분석 및 최적화
- 부분 중복 제거
- 이스케이프 분석
- GC 및 메모리 할당 최적화
- 동기화 최적화
Level 5 - 원시 코드 생성
원시 코드 생성 프로세스는 아키텍처에 따라 다양하다. 컴파일 단계 동안 메서드 트리가 → 시스템 코드 명령어로 변환된다. 특성에 따라 일부 소규모 최적화가 수행. 컴파일된 코드는 code cache라는 JVM 프로세스 공간 파트에 배치된다. 향후 호출 시 컴파일된 코드가 호출될 수 있도록 코드 캐시에 있는 메서드 위치가 기록된다.
임의의 지정된 시간에 JVM 프로세스는 JVM 실행 파일과 JVM의 바이트 코드 인터프리터에 동적을 ㅗ링크된 JIT 컴파일 코드 세트로 구성되어 있음
Reference
https://www.ibm.com/docs/ko/sdk-java-technology/8?topic=reference-jit-compiler