Execution Engine

실행 엔진(Execution Engine)은 메모리에 적재된 클래스(바이트 코드)들을 기계어로 변경하여 명령어(instruction) 단위로 실행한다. 바이트 코드를 운영체제에 맞게 해석해주는 역할을 수행한다. 실행 엔진이 바이트 코드를 명령어 단위로 읽어서 수행하는데 크게 두 가지 방식이 사용된다.

  1. 인터프리터
  2. JIT 컴파일러

동작 과정

  • JVM은 바이트코드를 실행할 때 처음부터 모든 코드를 기계어로 컴파일하지 않는다.
  • 초기에는 주로 인터프리터가 바이트코드 명령어를 하나씩 실행하면서 메서드 호출 횟수, 루프 반복 횟수 등의 실행 정보를 수집한다.
  • HotSpot JVM은 수집한 정보를 바탕으로 자주 호출되는 메서드나 반복적으로 실행되는 루프, 즉 Hot Spot을 식별한다.
  • JIT 컴파일러는 이렇게 자주 실행되는 코드만 선별하여 CPU가 직접 실행할 수 있는 네이티브 코드, 즉 기계어로 컴파일​한다. 거의 실행되지 않는 코드는 불필요하게 컴파일하지 않을 수 있다.
  • JIT 컴파일로 생성된 네이티브 코드는 JVM 프로세스의 메모리 영역인 Code Cache에 저장되고, 이후 해당 코드가 실행될 때 재사용된다.
  • Code Cache는 Java 객체가 저장되는 일반적인 Java Heap과는 별개의 영역이며, 현대 HotSpot JVM에서는 코드의 종류에 따라 여러 영역으로 나누어 관리된다.
  • 현대 HotSpot JVM은 일반적으로 계층형 컴파일을 사용한다. 인터프리터와 빠른 컴파일을 담당하는 C1 컴파일러, 높은 수준의 최적화를 담당하는 C2 컴파일러를 함께 사용하여 시작 속도와 최대 실행 성능 사이의 균형을 맞춘다.