Java compiler가 java 파일을 class 파일(byte code)로 변환시켜주고, OS가 byteCode를 이해할수 있도록 JVM이 해석해준다고 생각하시면 됩니다.Class Loder, RunTime Data Area, Execution engine으로 나눠지는데요.

모든 쓰레드 공유 영역
각 쓰레드 별 공유 영역
Method Area
Heap Area
Stack Area
PC Register
Stack Frame과 동일하게 스레드당 하나의 PC Register가 존재하고, 현재 수행중인 메서드 안에서 몇번째 라인을 실행해야하는지 주소값을 가지고있는 역할을 수행합니다.Native Method Stack
Stack Area가 아닌 Native Method Stack에 Stack Frame이 쌓인다. 이렇게 다른언어로 작성된 코드들을 Native Method Library라고한다.JVM Runtime Data Area에 배치된 바이트 코드는 Execution Engine에 의해 실행됩니다.interpreter 방식과 실행시점에 자주 쓸만한 코드들을 기계어로 변역 후 저장하여 사용하는 방식 JIT 컴파일러가 존재합니다.JIT 컴파일러 vs interpreter
JIT 컴파일러
바이트코드를 머신코드로 변환하는과정에서 머신 코드를 캐시하게 됩니다. 이로인해 반복되는 기계어 변역을 줄이기 때문에 성능이 향상되게 되고, 런타임 시점에 코드 최적화 해준다.
JIT컴파일러가 interperter방식 보다 평균 성능이 10~20배가 향상됩니다.
interpreter
캐시에 저장하기 때문에 실행하려는 바이트코드가 여러 번 반복되는 코드라면
JIT 컴파일러를 활용하여 많은 성능 향상을 볼 수 있겠지만, 한두 번 반복될 코드라면interperter의 방식으로 해석하는것이 효율적이다
이러한 장단점 때문에 JVM의 Execution Engine은 두개의 해석 방식을 모두 사용합니다.
추가로 Excution engine에서 사용되는 GC는 아래 포스팅에서 확인이 가능합니다.
위에서 언급했듯이 JIT Compiler는 cache에 머신코드를 저장하지만 어플리케이션이 시작되는 시점에는 캐시에 데이터가 없습니다.
warm up과 관련된 자세한 자료는 아래 링크에서 확인할수 있습니다.