본문 바로가기

JAVA/개념

JIT 컴파일러란 ?

728x90
반응형

자바 JIT 컴파일러 : 개요

 

JIT 컴파일(just-in-time compilation) 또는 동적 번역(dynamic translation)은 프로그램을 실제 실행하는 시점에 기계어로 번역하는 컴파일 기법이다.

 

전통적인 입장에서 컴퓨터 프로그램을 만드는 방법은 두 가지가 있는데, 인터프리트 방식과 정적 컴파일 방식으로 나눌 수 있다. 이 중 인터프리트 방식은 실행 중 프로그래밍 언어를 읽어가면서 해당 기능에 대응하는 기계어 코드를 실행하며, 반면 정적 컴파일은 실행하기 전에 프로그램 코드를 기계어로 번역한다.

 

JIT 컴파일러는 두 가지의 방식을 혼합한 방식으로 생각할 수 있는데, 실행 시점에서 인터프리트 방식으로 기계어 코드를 생성하면서 그 코드를 캐싱하여, 같은 함수가 여러 번 불릴 때 매번 기계어 코드를 생성하는 것을 방지한다.

 

최근의 자바 가상 머신과 .NET, V8(node.js)에서는 JIT 컴파일을 지원한다. 즉, 자바 컴파일러가 자바 프로그램 코드를 바이트코드로 변환한 다음, 실제 바이트코드를 실행하는 시점에서 자바 가상 머신이 바이트코드를 JIT 컴파일을 통해 기계어로 변환한다.

 

어떻게 작동합니까?!

JIT (Just-In-Time) 컴파일러는 런타임시 Java 애플리케이션의 성능을 향상시키는 Java Runtime Environment의 구성 요소입니다. Java 프로그램은 다양한 컴퓨터 아키텍처에서 JVM이 해석 할 수있는 플랫폼 중립 바이트 코드를 포함하는 클래스로 구성됩니다. 런타임에 JVM은 클래스 파일을로드하고 각 개별 바이트 코드의 의미를 결정하고 적절한 계산을 수행합니다. 해석 중 추가 프로세서 및 메모리 사용량은 Java 애플리케이션이 원시 애플리케이션보다 느리게 수행됨을 의미합니다. JIT 컴파일러는 런타임에 바이트 코드를 원시 기계 코드로 컴파일하여 Java 프로그램의 성능을 향상시킵니다.

JIT 컴파일러는 기본적으로 사용 가능하며 Java 메소드가 호출 될 때 활성화됩니다. JIT 컴파일러는 해당 메서드의 바이트 코드를 원시 기계 코드로 컴파일하여 "적시에"실행되도록 컴파일합니다. 메소드가 컴파일되면 JVM은 해석하는 대신 해당 메소드의 컴파일 된 코드를 직접 호출합니다. 이론적으로 컴파일에 프로세서 시간과 메모리 사용이 필요하지 않은 경우 모든 메서드를 컴파일하면 Java 프로그램의 속도가 네이티브 응용 프로그램의 속도에 접근 할 수 있습니다.
JIT 컴파일에는 프로세서 시간과 메모리 사용량이 필요합니다. JVM이 처음 시작되면 수천 개의 메소드가 호출됩니다. 이러한 모든 방법을 컴파일하면 프로그램이 결국 매우 우수한 최고 성능을 달성하더라도 시작 시간에 상당한 영향을 미칠 수 있습니다.

다양한 애플리케이션을위한 다양한 컴파일러

JIT 컴파일러는 두 가지 유형으로 제공되며 사용할 컴파일러를 선택하는 것이 종종 애플리케이션을 실행할 때 수행해야하는 유일한 컴파일러 조정입니다. 사실, 어떤 컴파일러를 선택 하려는지 아는 것은 Java가 설치되기 전에도 고려해야 할 사항입니다. 다른 Java 바이너리에는 다른 컴파일러가 포함되어 있기 때문입니다.

클라이언트 측 컴파일러

잘 알려진 최적화 컴파일러는 -clientJVM 시작 옵션을 통해 활성화되는 컴파일러 인 C1 입니다. 시작 이름에서 알 수 있듯이 C1은 클라이언트 측 컴파일러입니다. 사용 가능한 리소스가 적고 대부분의 경우 애플리케이션 시작 시간에 민감한 클라이언트 측 애플리케이션을 위해 설계되었습니다. C1은 코드 프로파일 링에 성능 카운터를 사용하여 간단하고 상대적으로 방해가되지 않는 최적화를 가능하게합니다.

서버 측 컴파일러

서버 측 엔터프라이즈 Java 애플리케이션과 같은 장기 실행 애플리케이션의 경우 클라이언트 측 컴파일러로는 충분하지 않을 수 있습니다. 대신 C2와 같은 서버 측 컴파일러를 사용할 수 있습니다. C2는 일반적으로 -server시작 명령 줄에 JVM 시작 옵션 을 추가하여 활성화됩니다 . 대부분의 서버 측 프로그램은 오랫동안 실행될 것으로 예상되므로 C2를 활성화하면 단기 실행되는 경량 클라이언트 응용 프로그램보다 더 많은 프로파일 링 데이터를 수집 할 수 있습니다. 따라서 더 고급 최적화 기술과 알고리즘을 적용 할 수 있습니다.

계층 형 컴파일

계층 형 컴파일은 클라이언트 측 및 서버 측 컴파일을 결합합니다. 계층 형 컴파일은 JVM의 클라이언트 및 서버 컴파일러 장점을 모두 활용합니다. 클라이언트 컴파일러는 애플리케이션 시작 중에 가장 활동적이며 낮은 성능 카운터 임계 값으로 트리거 된 최적화를 처리합니다. 또한 클라이언트 측 컴파일러는 성능 카운터를 삽입하고 고급 최적화를위한 명령어 세트를 준비합니다. 이는 나중에 서버 측 컴파일러에서 처리합니다. 계층 형 컴파일은 컴파일러가 영향이 적은 컴파일러 활동 중에 데이터를 수집 할 수 있으므로 나중에 고급 최적화에 사용할 수 있으므로 매우 리소스 효율적인 프로파일 링 방법입니다. 이 접근 방식은 또한 해석 된 코드 프로필 카운터 만 사용하는 것보다 더 많은 정보를 제공합니다.

코드 최적화

컴파일을 위해 메소드를 선택하면 JVM은 JIT (Just-In-Time 컴파일러)에 바이트 코드를 제공합니다. JIT는 메소드를 올바르게 컴파일하기 전에 바이트 코드의 의미와 구문을 이해해야합니다. JIT 컴파일러가 메서드를 분석 할 수 있도록 해당 바이트 코드는 먼저 다음과 같은 내부 표현으로 재구성됩니다.trees, 이는 바이트 코드보다 기계어 코드와 더 유사합니다. 그런 다음 분석 및 최적화가 메서드의 트리에서 수행됩니다. 결국 트리는 네이티브 코드로 변환됩니다. JIT 컴파일러는 둘 이상의 컴파일 스레드를 사용하여 JIT 컴파일 작업을 수행 할 수 있습니다. 다중 스레드를 사용하면 잠재적으로 Java 애플리케이션이 더 빨리 시작될 수 있습니다. 실제로 여러 JIT 컴파일 스레드는 시스템에 사용되지 않은 처리 코어가있는 경우에만 성능 향상을 보여줍니다. 기본 컴파일 스레드 수는 JVM에 의해 식별되며 시스템 구성에 따라 다릅니다. 결과
스레드 수가 최적이 아닌 경우 XcompilationThreads옵션 을 사용하여 JVM 결정을 대체 할 수 있습니다 . 이 옵션 사용에 대한 정보는 JIT 및 AOT 명령 행 옵션을 참조하십시오.

컴파일은 다음 단계로 구성됩니다.

 

인라이닝

인라인은 더 작은 메서드의 트리를 호출자의 트리에 병합하거나 "인라인"하는 프로세스입니다. 이렇게하면 자주 실행되는 메서드 호출 속도가 빨라집니다.

 

로컬 최적화

로컬 최적화는 한 번에 코드의 작은 부분을 분석하고 개선합니다. 많은 로컬 최적화는 클래식 정적 컴파일러에서 사용되는 검증 된 기술을 구현합니다.

 

제어 흐름 최적화

제어 흐름 최적화는 메서드 (또는 메서드의 특정 섹션) 내부의 제어 흐름을 분석하고 코드 경로를 재정렬하여 효율성을 향상시킵니다.

 

글로벌 최적화

전역 최적화는 전체 방법에서 한 번에 작동합니다. 더 "비싸고"더 많은 컴파일 시간이 필요하지만 성능이 크게 향상 될 수 있습니다.

 

네이티브 코드 생성

기본 코드 생성 프로세스는 플랫폼 아키텍처에 따라 다릅니다. 일반적으로이 컴파일 단계에서 메서드의 트리는 기계어 코드 명령어로 변환됩니다. 아키텍처 특성에 따라 몇 가지 작은 최적화가 수행됩니다.

 


 

인터프리터는 바이트 코드를 한줄한줄 읽으면서 OS가 실행할 수 있도록 기계어로 번역을 합니다. 초기 JVM은 인터프리터방식만 이용하여 한줄 한줄 읽기 때문에 실행속도가 느린 단점이 있었지만 JIT 컴파일러 방식을 통해 속도를 보완했습니다.

 

JIT 컴파일러는 자주 반복되는 코드를 기계어로 변환해서 캐싱을 해놓습니다. 이 캐싱된 기계어는 인터프리터가 해석을 하는 것이 아니라 JIT 컴파일러가 기계어로 변환해논걸 캐쉬에서 꺼내 바로 실행하니 빠른 것입니다. JIT 컴파일러와 인터프리터는 동시에 런타임 영역에서 다른스레드에서 실행됩니다.

 

JIT 컴파일러는 바이트코드를 nativecode로 바꾸기 때문에 실행이 빠르지만 nativecode로 변환하는데 비용이 발생합니다. 이런 변환 비용 때문에 jvm은 모든 코드를 JIT Compiler방식으로 실행하지 않고 인터프리터 방식을 사용하다 자주 사용되는 코드만 캐싱을하는 것입니다. JVM은 내부적으로 어떤 메서드가 얼마나 자주 수행되는지를 확인하고 HotSpot이라고 판단하면 컴파일을 수행해놓습니다.

 

이러한 과정을 통해 바이트코드는 JVM내부에서 컴퓨터가 실행할 수 있는 형태로 변경되어 실행되는 것입니다.

728x90
반응형