[JAVA] JVM과 자바 컴파일러

chaeyeong·2025년 1월 31일

자바 이론

목록 보기
1/9

1. JVM이란?

JVM(Java Virtual Machine Java)은 직역하자면 자바 프로그램을 실행시키기 위한 가상 머신이다. 자바는 OS에 종속적이지 않다는 특징이 있다. OS에 종속받지 않고 실행되기 위해선 OS 위에서 Java를 실행시킬 무언가가 필요한데 그것이 바로 JVM이다.


1-1. JVM의 구성요소

(1) Class Loader

  • JVM 내로 클래스 파일을 로드하고 링크를 통해 배치하는 작업을 수행하는 모듈이다. 런타임 시에 동으로 클래스를 로드한다.

(2) Execution Engine

  • Class Loader를 통해 JVM 내의 Runtime Data Area에 배치된 바이트코드들을 명령어 단위로 읽어서 실행한다.
  • 최초 JVM이 나왔을 당시에는 인터프리터 방식이었기 때문에 속도가 느리다는 단점이 있었지만 JIT 컴파일러 방식을 통해 이 점을 보완했다. JIT는 바이트코드를 어셈블러 같은 네이티브 코드로 바꿈으로써 실행이 빠르지만 역시 변환하는데 비용이 발생했다.
  • 이같은 이유로 JVM은 모든 코드를 JIT 컴파일러 방식으로 실행하지 않고, 인터프리터 방식을 사용하다가 일정한 기준이 넘어가면 JIT 컴파일러 방식을 실행한다.

(3) Garbage Collector

  • Garbage Collector는 Heap 메모리 영역에 생성된 객체들 중에서 참조되지 않은 객체들을 탐색 후 제거하는 역할을 한다.
  • 이 때 Garbage Collector가 역할을 하는 시간은 언제인지 정확히 알 수 없다.

(4) Runtime Data Area

  • JVM의 메모리 영역으로, 자바 애플리케이션을 실행할 때 사용되는 데이터들을 적재하는 영역이다.
  • 이 영역은 크게 Method Area, Heap Area, Stack Area, PC Register, Native Method Stack으로 나눌 수 있다.

  • Method Area

    • JVM에서 읽어들인 클래스, 인터페이스, 메소드,필드, static 변수 등의 바이트코드를 보관한다.
  • Heap Area

    • 프로그램 상에서 데이터를 저장하기 위해 런타임 시 동적으로 할당하며 사용하는 메모리 영역이다.
    • new 연산자를 통해 생성한 객체 또는 인스턴스와 배열을 저장하며, JVM이 관리한다.
  • Stack area

    • 메소드 호출 시 생성되는 스레드 수행정보를 기록하는 프레임을 저장한다.
    • 메소드 정보, 지역변수, 매개변수, 연산 중 발생하는 임시 데이터를 저장한다.
  • PC Register

    • 현재 실행중인 JVM 주소를 가지고 있다.
    • CPU instruction을 수행하는 동안 필요한 정보를 CPU 내 기억장치인 레지스터에 저장한다.
    • 연산 및 결과값을 메모리에 전달하기 전 CPU 내 기억장치이다.
  • Native method stack

    • 자바 외의 언어로 작성된 네이티브 코드를 위한 메모리 영역


1-2. JVM의 실행과정

  • 자바는 가상 머신을 거쳐 OS에 도달하기 때문에 OS가 인식할 수 있는 기계어로 바로 컴파일 되지 않는다.

  • java compiler가 .java 파일을 .class 라는 java bytecode로 변환한다. 변환된 코드는 기계어가 아니기 때문에 OS에서 바로 실행되지 않는다.

  • JVM이 OS가 bytecode를 이해할 수 있도록 해석해준다. OS에 상관없이 실행되므로 OS에 종속적이지 않다.

  • 다른 애플리케이션의 코드는 OS만 거치는데 자바 애플리케이션은 JVM을 한 번 더 거친다. 실행 시에 해석이 되기 때문에 속도가 느리다는 단점이 있다.
    -> 이 단점을 보완하기 위해 나온 것이 JIT 컴파일러


2. 바이트코드(Bytecode)란?

자바 가상머신이 이해할 수 있는 언어로 자바 소스코드, 자바 컴파일러로 변환되는 코드의 명령어 크기가 1바이트라서 바이트코드 라고 불린다.

자바는 JVM이 이해할 수 있는 언어인 바이트코드 형태로 제공되고,
바이트코드와 JVM이 있으면 어느 운영체제에도 종속되지 않고 실행된다.


3. 실행엔진

  • 인터프리터(Interpreter) : 바이트코드 명령어를 하나씩 읽어서 해석하고 실행한다. 하나씩 읽어서 전체를 실행하기 때문에 속도가 느리다.

  • JIT 컴파일러 : 인터프리터의 단점을 보완하기 위해 도입된 방식으로 바이트코드 전체를 컴파일하여 바이너리 코드로 변경하고, 이후에는 해당 메소드를 더 이상 인터프리팅 하지 않고, 바이너리 모드로 직접 실행하는 방식이다. 인터프리팅보다 상대적으로 속도가 빠르다.


4. JIT 컴파일러

JIT(Just In Time) 컴파일러는 런타임 시 바이트코드를 원시 시스템 코드로 컴파일하여 자바 애플리케이션의 성능을 향상시키는 런타임 환경의 컴포넌트이다.

4-1. JIT 컴파일러의 개념

  • 전체 코드 실행 속도가 느린 단점이 있는 인터프리터 방식의 문제를 해결하기 위해서 나온 방법이다.

  • 기존 클래스파일을 실행하는 방법은 인터프리터 방식이 기본이지만,
    JIT 컴파일러는 동적 변환이라고 볼 수 있다.

  • 런타임 시 클래스 스타일을 네이티브 기계어로 한방에 컴파일 후 사용하는 개념이다.

4-2. 성능

  • JIT 컴파일러는 다른 컴파일러와는 달리 프로그램이 시작되고 코드를 컴파일 하고난 후에 실행된다.

  • 바이트코드는 CPU가 제대로 이해할 수 있는 언어로 컴파일되고 번역되어야 하는데, 이 번역 방법은 애플리케이션의 속도와 성능에 큰 영향을 미칠 수 있다.

  • 성능을 개선하기 위해 JIT 컴파일러는 런타임에 적절한 바이트코드 시퀀스를 기계코드로 컴파일 한다.

  • JVM이 처음 시작되면 수 천 가지 메소드가 호출되는데, 모든 메소드를 컴파일하면 프로그램이 최대 성능을 달성하더라도 시작 시간에 상당한 영향을 줄 수 있다.

  • 각 메소드에 대해 JVM은 사전 정의된 컴파일 임계값에서 시작하여 메소드가 호출될 때마다 감소되는 호출 계수를 정의한다.

  • 호출 계수가 0에 도달하면 메소드에 대한 JIT 컴파일이 시작된다. 따라서 자주 사용되는 메소드는 JVM이 시작된 직후에 컴파일 되며, 사용되지 않는 메소드는 훨씬 나중에 컴파일 되거나 전혀 컴파일되지 않는다.

  • JIT 컴파일 임계값은 JVM이 빨리 시작하고 성능이 향상되도록 도와준다. 임계값은 시작시간과 장기간 성능 사이의 최적의 균형을 얻기 위해 선택되었다.

  • JIT 컴파일러는 바이트코드를 일단 중간 단계의 표현인 IR(Intermediate Representation)로 변환하여 최적화를 수행하고, 그 다음에 네이티브 코드를 생성한다.


5. JDK와 JRE의 차이

  • JRE
    • 자바 어플리케이션을 생성하고 수행하기 위한 소프트웨어
    • 자바 프로그램을 실행할 수 있는 환경을 제공
  • JDK
    • JRE를 포함하여 개발에 필요한 것들을 모아놓은 더 큰 범위의 소프트웨어
    • 자바 프로그램 개발과 실행할 수 있는 환경을 제공
    • 자바 애플리케이션을 개발하기 위해서는 JDK를 필수로 설치해야 한다
profile
그래도 해야지 어떡해

0개의 댓글