JVM, JRE, JDK 간단 개념
JVM
- 컴파일된 바이트 코드(.class)파일을 OS에서 실행 가능한 코드로 변환하여 실행한다. 코드 변환을 위해서 인터프리터와 JIT 컴파일러가 동작한다.
- JVM은 표준스팩이 존재하며 스팩에 따라 구현한 여러 구현체들이 있다.
- OS에 종속적이기 때문에 윈도우용 JVM, 맥용 JVM 이런식으로 여러 종류가 있다.(밴더에 따라서도 구분됨)
- JVM은 단독으로 배포되지 않고 JRE에 포함되어 배포된다.
JRE
- Library를 포함하여 자바프로그램 실행에 필요한 것들을 모아둔 배포판이다.
- JVM과 핵심 라이브러리, 프로퍼티, 리소스 파일 등을 가지도 있다.
JDK
- JRE + 개발에 필요한 툴
JVM Architecture
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로 지정한 디렉터리의 클래스 파일을 로드한다. 보통 개발한 클래스 파일들이 여기서 로드된다.
로드한 클래스를 찾을 때는 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에 의해 정리된다.