본문 바로가기
JVM

실행 엔진

by samryong 2024. 4. 23.

1. Interpreter

- 실행되는 바이트 코드를 한줄 한줄 해석한 후 실행시키는 일을 합니다.

 

2. JIT Compiler

Interpreter로만 application이 실행된다고 생각했을 때 성능적으로 문제가 있다 위에 짧게나마 Interpreter의 설명을 했는데 실행되는 코드들을 Run-time 환경에서 기계어로 변환하고 변환한 기계어를 실행시킨다고 했을때 다른 컴파일 언어들과 비교했을 때 성능적으로 차이가 있을 수 밖에 없습니다. 이러한 단점을 보완하기 위해서 JDK 1.3부터 JIT Compiler를 도입하게 되었습니다. 

JIT Compiler는 자주 사용되는 코드를 JIT Compiler에 존재하는 profiler가 관찰을하고 분석을 하여 단계별로 컴파일을 진행하며 특정 단계를 제외하고는 Code cache(메모리)에 컴파일된 코드를 저장해 놓아서 Interpreter만 사용했을 때 보다 빠르게 실행할 수 있도록 도와줍니다.

 

compile 방식

- c1(client compiler): client 어플리케이션에 좀 더 적합한 compile 방식으로 최적화는 낮은 수준이지만 컴파일 시간은 더 빠릅니다. 

- c2(server compiler): 더 오랜시간 동안 컴파일이 되는 만큼 최적화가 잘되어 컴파일 이후 사용될 때 더 빠르게 실행 됩니다.

- Tiered Compilation : C1 과 C2를 같이 사용하므로써 컴파일의 5단계를 거쳐 효율적으로 컴파일과 캐시를 하도록하는 방식입니다.

tiered compilation(c1ff + c2)

 

Java7부터 릴리즈 되었으며 Java8 버전 이후로는 c1과 c2가 함께 사용되는 tired compiler가 별다른 설정이 없으면 적용됩니다. 

Tiered Compilation은 C1 컴파일러와 C2 컴파일러를 순차적으로 사용하는 방식으로 Level 0부터 Level 4까지 단계가 이어지는데 각 계층별 간단하게 어떤 작업이 일어나는지 소개하겠습니다. 

 

 - Level 0(Interpreted Code) : JVM은 초기에 모든 코드를 Interpreter를 통해서 실행합니다. 여기에서는 컴파일과 Code cache 저장을 수행하지 않습니다. JIT compiler는 이 단계에서 profiling 정보를 활용하여 최적화를 수행합니다.

 

 - Level 1(Simple C1 Compiled code) : C1 Compiler를 사용하여 코드를 컴파일하지만 복잡성이 낮다고 판단하기 때문에 profiling 정보는 수집하지 않습니다. JVM은 비교적 간단한 메소드들에 Level 1을 사용합니다.

 

 - Level 2: JVM은 경량 profiling과 함께 C1 Compiler를 사용하여 코드를 컴파일합니다.

- Level 3: JVM이 전체 profiling을 통해 C1 Compiler를 사용하여 컴파일을 수행하는 단계입니다. c2의 대기열이 가득 찼을 때 사용되는 단계입니다. 가능한 빨리 코드를 컴파일하여 성능을 향상시키는 것이 목표입니다.

 

- Level 4: JVM은 장기적으로 최대 성능을 위해 C2 Compiler를 사용하여 코드를 컴파일합니다. 이 단계에서 생성된 코드는 완전히 최적화된 것으로 간주되므로, 더 이상 profiling 정보를 수집하지 않습니다. 그러나 필요에 따라 코드를 디옵티마이즈하고 레벨 0으로 되돌릴 수도 있습니다.

 

Tiered Compliation을 적용하지 않고 c1이나 c2를 설정값으로 지정해서 사용할 수 있고 c1이나 c2를 사용할 때는

"-XX:CompileThreshold = 값 "으로 임계치 설정해 줄 수 있고 Tiered Compilation을 사용할 때는 각 단계별로 임계치를 설정해 줄 수 있습니다. 

ex) -XX:Tier4CompileThreshold = 10000

현재 사용중인 Java 버전에서 사용되는 기본 임계값을 확인하려면 java -XX:+PrintFlagsFinal -version | grep CompileThreshold 

명령어를 사용하여 확인 할 수 있습니다.

 

ex) java -XX:+PrintFlagsFinal -version | grep CompileThreshold
intx CompileThreshold = 10000
intx Tier2CompileThreshold = 0
intx Tier3CompileThreshold = 2000
intx Tier4CompileThreshold = 15000

 

이렇게 Level 4까지 단계별로 간단하게 알아봤습니다. 추가로 알려드릴 것은 컴파일이 되지 않는 Level 0을 제외하고는 모두(Level 1,2,3,4)는 Code cache에 저장될 수 있고 JVM8부터 자동으로 적용되는데 설정으로 Tiered Compilation을 적용하지 않을 수 있습니다.

 

Code Cache

JIT compiler에 의해 컴파일된 코드와 JVM 자체 네이티브 코드, Interpreter 관련된 코드들이 저장된 메모리 영역입니다. Code Cache는 메모리 영역이기 때문에 할당할 수 있는 공간이 한정적이고 명령어를 통해서 할당 영역을 조절할 수 있습니다.

JVM8까지는 Code Cache는 하나로 관리가 되었으며 JVM9부터는 Tiered Compilation으로 인해 비계층 컴파일을 사용할 Code Cache의 양이 2배~4배 정도 증가 하게되면서 Code Cache의 중요성이 높아져 Code Cache의 영역을 세가지로 분할하게 되었습니다. 분할된 코드 캐시는 코드의 지역성(Code Locality)을 향상시키고 메모리 단편화(Memory Fragmentation)를 줄임으로써 성능을 개선하는 데 도움을 줍니다.

 

Code Cache의 세가지 영역

- none-method segment : JVM 내부 관련코드를 저장하는 영역입니다.

- profiled-code segment : 수명이 짧은 C1 컴파일 코드를 저장하는 영역입니다.

- non-profiled segment :  수명이 긴 C2 컴파일 코드를 저장하는 영역입니다.

 

 

3.GC(Gabage Collector)

 

'JVM' 카테고리의 다른 글

JVM 메모리 구조  (0) 2024.03.13
Class Loader  (0) 2024.03.13
JVM 동작의 흐름  (0) 2024.03.08