JVM, JRE, JDK 간단 개념

JVM

  • 컴파일된 바이트 코드(.class)파일을 OS에서 실행 가능한 코드로 변환하여 실행한다. 코드 변환을 위해서 인터프리터와 JIT 컴파일러가 동작한다.
  • JVM은 표준스팩이 존재하며 스팩에 따라 구현한 여러 구현체들이 있다.
  • OS에 종속적이기 때문에 윈도우용 JVM, 맥용 JVM 이런식으로 여러 종류가 있다.(밴더에 따라서도 구분됨)
  • JVM은 단독으로 배포되지 않고 JRE에 포함되어 배포된다.

JRE

  • Library를 포함하여 자바프로그램 실행에 필요한 것들을 모아둔 배포판이다.
  • JVM과 핵심 라이브러리, 프로퍼티, 리소스 파일 등을 가지도 있다.

JDK

  • JRE + 개발에 필요한 툴

JVM Architecture

jvm0

JVM은 컴파일된 클래스파일(바이트 코드)을 OS 위에서 실행하며 실행을 위해 다음 3가지 파트로 나눠진다.

1. 클래스로더 시스템

Loading, Linking, Initialization 3가지 동작으로 클래스를 로드하고 정보를 저장한다.

  • Loading : 클래스 파일을 읽고 클래스 정보를 메소드 영역에 저장한다. 로딩이 완료된 클래스는 해당 클래스 타입의 Class 객체를 생성하여 힙 영역에 저장한다.
  • Linking : Verify, Prepare, Resolve 3가지 단계로 이루어져 있다.
  • Initialization : static 변수의 값을 할당하고 static 블록을 실행한다.

클래스파일(.class)을 로드하는 클래스로더(ClassLoader)는 계층형 구조를 가진다.

  • Bootstrap ClassLoader : $JAVA_HOME/jre/lib/rt.jar 의 클래스 파일들을 로드한다. JVM을 실행하기 위한 핵심 파일들이 로드된다.
  • Extension ClassLoader : $JAVA_HOME/jre/lib/ext 또는 java.ext.dirs 옵션에 정의된 디렉터리의 클래스 파일들을 로드한다.
  • Application ClassLoader : Classpath로 지정한 디렉터리의 클래스 파일을 로드한다. 보통 개발한 클래스 파일들이 여기서 로드된다.

jvm1

로드한 클래스를 찾을 때는 Application ClassLoader를 시작으로 해서 못찾으면 상위 ClassLoader에게 찾는 클래스를 위임한다.
최상단의 Bootstrap ClassLoader에서도 클래스를 찾지 못하면 익숙한 ClassNotFoundException 이 발생한다.

2. 다양한 메모리

  • 메소드 영역

클래스 레벨의 정보들이 저장된다.(클래스 이름, 부모 클래스 이름, 메서드, 변수, static 변수 등의 정보)
JVM마다 오직 하나의 메소드 영역을 가지며 공유자원이다.
공유자원 : 다른 영역의 메모리에서도 참조가 가능하다.

  • 힙 영역

모든 객체(인스턴스)들의 정보가 힙에 저장된다.
JVM마다 하나의 힙 영역을 가지며 메소드 영역과 같이 공유자원이다. 메소드, 힙 영역은 공유자원으로 여러 쓰레드에서 접근하여 데이터를 가져갈 수 있다. (thread-safe 하지 않는다.)

  • 스택 영역

JVM은 쓰레드마다 하나의 런타임 스택을 생성해주며 이 스택들이 스택 영역에 저장된다.
쓰레드가 가진 스택은 메서드 콜들이 스택 프레임이라 부르는 블럭으로 쌓여있다.(오류가 발생하면 콘솔에 찍히는 그 형태)
공유자원이 아니므로 Thread-safe하며 Thread가 종료될 때 해당 스택은 JVM에 의해 제거된다.

  • PC 레지스터 영역

각 쓰레드마다 분리된 PC 레지스터를 가지며 현재 실행중인 스택 프레임을 가리키는 포인터가 저장된다. 쓰레드가 다음 실행 스택 프레임으로 넘어가면 PC 레지스터도 업데이트 된다.

  • 네이티브 메소드 스택 영역

각 쓰레드마다 분리된 네이티브 메소드 스택 영역을 가진다. 네이티브 메소드 정보가 저장된다.
네이티브 메소드 : 메소드 선언에 native 키워드가 붙어있고 구현은 C나 C++같이 자바가 아닌 다른 언어로 구현되어 있는 메소드

3. 실행 영역

인터프리터, JIT 컴파일러, Garbage Collector로 구성되어 있다.

  • 인터프리터 : 바이트코드는 인터프리터로 한줄씩 네이티브 코드로 바꿔가며 어플리케이션이 실행된다.
  • JIT 컴파일러 : 매번 라인을 새로 읽으며 실행하지 않고 반복되는 코드는 JIT 컴파일러가 모두 네이티브 코드로 바꿔둔다. (효율성을 위해서)
  • GC : 더이상 사용되지 않는(참조되지 않는) 객체들은 GC에 의해 정리된다.