본문 바로가기

CS study/java

[JVM]JVM과 ClassLoader 그리고 JIT의 관계

목차

    개요

     

    JVM에서 컴파일러에 의해 번역된 Class 바이트코드들은 jar이 빌드되면 '부트스트랩 클래스로더' - '확장 클래스로더' - '시스템 클래스로더'를 읽어가면서 Runtime Area에 메타데이터와 Class 정보들을 저장한다.

     

    이 방식만 보면 Java는 인터프리터라고 할 수 있을 것 같은데 왜 컴파일 방식을 혼합하여 사용한다고 하나? 라는 질문을 보았다. 이에 대해 조사하고, 답변하고자 한다.

     

    Java가 인터프리터가 아닌 이유

    Java는 "컴파일된 인터프리터 언어"로 종종 설명된다.

    이는 Java 프로그램이 바이트코드로 컴파일된 후, Java Virtual Machine(JVM)에서 실행되기 때문이다.

     

    Java Virtual Machine(JVM)에서 바이트코드를 처리하는 방식은 실제로 컴파일러와 인터프리터의 중간 형태라고 볼 수 있다.

     

    1. 초기에는 JIT 컴파일러가 Hotspot Compiler의 캐시 코드가 없기에(저장된 것이 없음) 모든 바이트코드를 한줄한줄 해석한다. 이는 전통적인 인터프리터와 유사하다.

    2. 반복 구문이 사용되고, 자주 사용되는 바이트코드가 보일 때마다 JIT는 이를 Hotspot Compiler에 저장한다.

    (이 코드들을 캐싱한다.)

    3. 이후 이 저장된 코드 재사용 시, JIT의 Hotspot Compiler에서 캐시된 코드를 대체하여 사용한다. 이는 전통적인 컴파일러의 동작 과정과 유사하다.

     

    JIT 컴파일러의 역할


    JIT 컴파일러는 실행 시간(runtime)에 바이트코드를 기계어로 변환하는 역할을 한다.

     

    JIT 컴파일러에 의한 컴파일 작업은 프로그램 실행 중에 계속해서 수행될 수 있으며, 변환된 기계어 코드는 메모리에 캐시된다.

    이후 동일한 코드가 다시 실행될 때는 해석하는 대신 이 캐시된 기계어 코드를 직접 실행하여 성능을 향상시킨다.

     

    캐시 사용

    이전 답변의 연장선이다.

    JIT 컴파일러는 자주 사용되는 코드(핫 스팟)를 식별하고, 이를 기계어로 컴파일하여 메모리에 캐시한다.

    이렇게 하면 바이트코드를 매번 해석할 필요 없이 빠르게 실행할 수 있다.

     

    클래스 로더와의 관계? 

    클래스 로더에 의해 로드된 코드가 실행될 때 JIT 컴파일러에 의해 최적화

     

    클래스 로더는 .class 파일(바이트코드)을 JVM의 메소드 영역(Method Area)에 로드하는 역할을 한다.

    JIT 컴파일러는 이렇게 로드된 바이트코드 중에서 실행 시간에 자주 사용되는 코드를 컴파일하고 캐시한다.

     

    따라서 JIT 컴파일러에 의해 캐시되는 것은,

    "클래스 로더에 의해 로드된 .class 바이트코드 중에서 자주 사용되는(핫 스팟) 코드" 이다.

    JIT에 의해 저장되는 것

    JIT 컴파일러에 의해 저장되는 것은 컴파일된 기계어 코드이다. JIT 컴파일러는 다음과 같은 단계를 통해 작업을 수행한다.

     

    1. 프로파일링과 분석: 프로그램이 실행되면서, JVM은 코드의 실행 패턴을 모니터링하고, 어떤 부분이 자주 실행되는지(핫 스팟) 분석한다.
    2. 컴파일: 자주 사용되는 바이트코드(핫 스팟)는 JIT 컴파일러에 의해 플랫폼 특정 기계어로 컴파일된다. 이 과정에서 다양한 최적화 기법이 적용될 수 있다.
    3. 캐싱: 컴파일된 기계어 코드는 메모리에 저장되어, 동일한 바이트코드가 다시 실행될 때 빠르게 접근하고 실행할 수 있다.

    컴파일된 코드에는 다음과 같은 것들이 포함될 수 있다.

    1. 메서드의 기계어 코드: 자주 호출되는 메서드
    2. 루프의 기계어 코드: 반복적으로 실행되는 루프
    3. 조건 분기 최적화: 실행 패턴에 기반한 조건문의 예측 및 최적화
    4. 인라인화: 자주 호출되는 작은 메서드의 바디를 호출 지점에 직접 삽입

    JIT 컴파일러의 목적은 프로그램의 실행 속도를 향상시키는 것이다.

    비록 캐싱과 바이트코드 해석(컴파일러 변환) 시간 때문에 초기 실행 속도가 느릴 수 있지만, 프로그램이 계속 실행됨에 따라 JIT 컴파일러가 최적화된 기계어 코드를 생성하고 캐시하기 때문에 성능이 점차 향상다.