HotSpot VM
- 자바 성능 개선을 위해 Sun에서 만든 Just In Time(JIT) 컴파일러의 이름이 HotSpot VM
- JIT 컴파일러는 프로그램의 성능에 영향을 주는 지점에 대해서 지속적으로 분석하며, 분석된 지점은 부하를 최소화하고, 높은 성능을 내기 위한 최적화 대상이 된다.
- 자바 1.3 버전부터 기본 VM으로 사용되어 왔으며, 운영되고 있는 대부분의 시스템들은 모두 HotSpot 기반의 VM
- HotSpot VM은 아래의 세 가지 주요 컴포넌트로 구성된다.
- VM 런타임
- JIT 컴파일러
- 메모리 관리자
VM 런타임
- HotSpot VM 런타임에 GC 방식과 JIT 컴파일러를 끼워 사용할 수 있다.
- 이를 위해서 VM 런타임은 JIT 컴파일러용 API와 가비지 컬렉터용 API를 제공한다.
- JVM을 시작하는 런처와 스레드 관리, JNI 등도 VM 런타임에서 제공한다.
JIT Optimizer
- 컴파일이라는 작업은 상위 레벨의 언어로 만들어진 코드를 기계에 의존적인 코드로 변환하는 것을 말한다.
- 자바는 javac라는 컴파일러를 사용하며, 이 컴파일러는 자바 클래스 파일를 바이트 코드로 된 class파일로 변환한다.
- JVM은 항상 바이트 코드로 시작하며, 동적으로 기계에 의존적인 코드로 변환된다.
- JIT는 애플리케이션에서 각각의 메서드를 컴파일할 만큼 시간적 여유가 많지 않으므로, 모든 코드는 초기에 인터프리터에 의해서 시작되고, 해당 코드가 충분히 많이 사용될 경우에 컴파일할 대상이 된다.
- HotSpot VM에서 이러한 작업은 각 메서드에 있는 카운터를 통해서 통제되며, 메서드에는 두 개의 카운터가 존재한다.
- invocation counter: 메서드를 시작할 때마다 증가
- backedge counter: 높은 바이트 코드 인덱스에서 낮은 인덱스로 컨트롤 흐름이 변경될 때마다 증가
- backedge counter는 메서드 루프가 존재하는지를 확인할 때 사용되며, invocation counter보다 컴파일 우선순위가 높다.
- 이 카운터들이 인터프리터에 의해서 증가될 때마다 그 값들이 한게치에 도달했는지 확인하고, 도달했을 경우 인터프리터는 컴파일을 요청한다.
- invocation counter가 사용하는 한계치는 CompileThreshold
- backedge counter가 사용하는 한계치는 CompileThreshold * OnStackReplacePercentage / 100
- 컴파일이 요청되면 컴파일 대상 목록의 큐에 쌓이고, 하나 이상의 컴파일러 스레드가 이 큐를 모니터링한다.
- 만약 컴파일러 스레드가 바쁘지 않으면 큐에서 대상을 빼내 컴파일을 시작한다.
- 보통 인터프리터는 컴파일이 종료되기를 기다리지 않는 대신, invocation counter를 리셋하고 인터프리터에서 메서드 수행을 계속 한다.
- 컴파일이 종료되면, 컴파일된 코드와 메서드가 연결되어 이후 부터는 메서드가 호출되면 컴파일된 코드를 사용한다.
OSR
- HotSpot VM은 OSR라는 특별한 컴파일도 수행하는데, OSR은 인터프리터에서 수행한 코드 중 오랫동안 루프가 지속되는 경우 사용된다.
- 만약 해당 코드의 컴파일이 완료된 상태에서 최적화되지 않은 코드가 수행되고 있는 것을 발견한 경우, 인터프리터에 계속 머무르지 않고 컴파일된 코드로 변경한다.
※ 참고: Java 5 HotSpot VM에 추가된 기능
- JVM이 시작될 때 플랫폼과 시스템 설정을 평가하여 자동으로 GC를 선정하고, 자바 힙 크기와 JIT 컴파일러를 선택한다.
- 이 기능을 통해서 애플리케이션의 활동과 객체 할당 비율에 따라 GC가 동적으로 자바 힙 크기를 조절하며, New의 Eden과 Suvivor, Old 영역의 비율을 자동으로 조절한다.
JIT 컴파일 및 최적화 절차
JRockit
- JVM은 각 OS에서 작동할 수 있도록 바이트 코드를 입력 값으로 받아 각종 변환을 거친 후 해당 칩의 아키텍처에서 잘 돌아가는 기계어 코드로 변환되어 수행된다.
- JRockit은 아래와 같은 최적화 단계를 거친다.
- JIT 컴파일
- thread 모니터
- JRockit에는 sample thread라는 스레드가 존재하며, 주기적으로 애플리케이션의 스레드를 점검한다.
- 이 스레드는 어떤 스레드가 동작 중인지의 여부와 수행 내역을 관리한다.
- 이러한 정보들을 통해 어떤 메서드가 많이 사용되는지를 확인하여 최적화 대상을 찾는다.
- 최적화
- sample thread가 식별한 대상을 최적화한다.
- 이 작업은 백그라운드에서 진행되며, 수행중인 애플리케이션에 영향을 주지 않는다.
IBM JVM
- 인라이닝
- 메서드가 단순할 때 적용되는 방식으로, 호출된 메서드가 단순할 경우 호출된 메서드의 내용이 호출한 메서드의 코드에 포함된다.
- 자주 호출되는 메서드의 성능이 향상된다는 장점
- 지역 최적화
- 작은 단위의 코드를 분석하고 개선하는 작업을 수행한다.
- 조건 구문 최적화
- 메서드 내의 조건 구문을 최적화하고, 효율성을 위해 코드의 수행 경로를 변경한다.
- 글로벌 최적화
- 메서드 전체를 최적화하는 방식
- 컴파일 시간이 많이 소요된다는 단점이 있지만, 많은 성능 개선을 할 수 있다는 장점
- 네이티브 코드 최적화
- 플랫폼 아키텍처에 의존적이며, 아키텍처에 따라서 최적화를 다르게 처리한다.
- 컴파일된 코드는 Code cache라고 하는 JVM 프로세스 영역에 저장되며, 결과적으로 JVM 프로세스는 JVM 수행 파일과 컴파일된 JIT 코드의 집합으로 구분된다.
JVM 동작 과정
클래스 로더
'Java > Java Tuning' 카테고리의 다른 글
reflection (0) | 2022.03.28 |
---|---|
Garbage Collector (GC) (0) | 2022.01.31 |
서버 세팅 (0) | 2022.01.22 |