JVM, JDK, JRE의 차이를 알아보자.
JVM : Java Virtual Machine
- 자바 가상 머신
- 자바 바이트코드 (.class파일)를 인터프리터와 JIT 컴파일러가 OS에 특화된 코드로 변환하여 실행
- JVM 밴더: 오라클, 아마존, Azul 등
- 특정 플랫폼에 종속적
JRE : Java Runtime Environment
- JRE = JVM + Library
- 핵심 라이브러리, 자바 런타임 환경에서 사용하는 프로퍼티 세팅, 리소스 파일 등을 가지고 있음
- 자바 애플리케이션을 실행할 수 있도록 구성된 배포판
- 개발관련 도구는 제공 되지 않음(컴파일러 javac가 JRE에 포함되어 있지 x)
JDK : Java Development Kit
- JDK = JRE + 개발에 필요한 툴
- 개발 툴 : javadoc, jar, javap 등등
- java11부터 module system 도입. JRE를 따로 제공하지 않게 됨.
- 소스 코드 작성시 사용하는 자바 언어는 플랫폼에 독립적
JVM의 구조
Stack, PC, Native Method Stack : 특정 쓰레드에 국한된다.
Heap, Method : 공유자원
클래스 로더 시스템
클래스 로더 시스템은 .class에서 바이트 코드를 읽고 메모리에 저장한다.
loading -> linking -> initialization 순서대로 진행
1. loading : 클래스를 읽어오는 과정
- 클래스 로더가 .class파일을 읽는다 -> 그 내용에 따라 적절한 바이너리 데이터를 만든다 -> method 영역에 저장한다.
- 메소드 영역에 저장하는 데이터는
- FQCN : Fully Qualified Class Name
- 클래스인지, 인터페이스인지, 이넘인지
- 메소드와 변수
- 로딩이 끝나면 해당 클래스 타입의 Class 객체를 생성하여 heap 영역에 저장
로딩 클래스 로더의 종류
- 부트스트랩 클래스 로더 (BootLoader)
- JAVA_HOME\lib에 있는 코어 자바 API 제공.
- 최상위 우선순위를 가진 클래스 로더
- 네이티브 코드로 구성되어 있음
- 플랫폼 클래스 로더 (PlatformClassLoader)
- JAVA_HOME\lib\ext 폴더 또는 java.ext.dirs 시스템 변수에 해당하는 위치에 있는 클래스를 읽음
- 애플리케이션 클래스 로더 (AppClassLoader)
- application classpath(application 실행할 때 주는 -classpath 옵션 또는 java.class.path 환경 변수의 값에 해당하는 위치)에서 클래스를 읽음
2. linking : 레퍼런스를 연결하는 과정
- Verify, Prepare, Resolve 세 단계
- Verify : .class 파일 형식이 유효한지 체크
- Prepare : 클래스 변수(static 변수)와 기본값에 필요한 메모리 준비
- Resolve(optional) : symbolic memory reference를 메소드 영역에 있는 실제 레퍼런스로 교체 (실제 heap에 들어있는 인스턴스를 가리키도록 하는 것)
3. Initialization : static 값들을 초기화하고 변수에 할당하는 과정
- static 변수의 값을 할당. static 블럭이 있다면 이때 실행된다.
런타임 데이터 영역 (메모리)
Method
- 클래스 수준의 정보 저장 (클래스 이름, 부모 클래스 이름, 클래스 경로, 메소드, 변수)
- JVM당 하나만 생성된다.
- 다른 영역에서도 참조 가능하다. 인스턴스 생성에 필요한 정보도 존재하기 때문에 JVM의 모든 Thread들이 Method Area를 공유한다.(공유자원)
- JVM의 다른 메모리 영역에서 해당 정보에 대한 요청이 오면, 실제 물리 메모리 주소로 변환해서 전달해준다.
- JVM 구동 시작 시에 생성되고, 종료할 때까지 유지되는 공통 영역이다.
Heap
- 객체를 저장. (공유자원)
- 코드 실행을 위한 Java로 구성된 객체 및 JRE 클래스들이 탑재된다.
- 문자열에 대한 정보를 가진 String pool, 실제 데이터를 가진 인스턴스, 배열 등이 저장된다.
- JVM당 하나만 생성된다.
- heap영역이 가진 데이터는 모든 Java Stack 영역에서 참조되어 Thread간 공유가 된다.
- heap영역이 가득 차면 OutOfMemoryError를 발생시킨다.
- 참조되지 않는 인스턴스와 배열에 대한 정보도 얻을 수 있어, GC의 주 대상이 된다.
Stack
- 각 Thread별로 따로 할당되는 영역.
- heap 메모리 영역보다 비교적 빠르다.
- Thread 별로 메모리를 따로 할당하기 때문에 동시성 문제에서 자유롭다.
- 쓰레드마다 런타임 스택을 만든다. 그 안에 메소드 호출을 스택 프레임이라는 블럭으로 쌓는다. 쓰레드 종료시 런타임 스택도 사라진다.
- 각 Thread들은 메소드를 호출할 때마다 프레임이라는 단위를 push하게 된다.
- 메소드가 마무리되며 결과를 반환할 시 해당 프레임은 stack으로부터 pop된다.
- 프레임은 메소드에 대한 정보를 가지고 있는
local variable
,operand stack
,constant pool reference
로 구성 local variable
: 메소드 안의 지역 변수들을 가지고 있다.operand stack
: 메소드 내 연산을 위한 바이트 코드 명령문들이 들어있다.constant pool refence
: 참조를 위한 공간
PC (Program Counter) Register
- 쓰레드마다 각각 자신만의
PC Register
를 가지고 있다. 쓰레드 내에서 현재 실행할 스택 프레임을 가리키는 포인터가 생성된다. - 쓰레드별로 동시에 실행하는 환경이 보장되어야 하니, 최근에 실행중인 JVM에서 명령어 주소값을 저장할 공간이 필요한데, 이 공간을 PC Register가 관리하여 추적해준다.
- 실행했던 메소드 != 네이티브이면 JVM에서 사용된 명령의 주소를 저장하게 된다.
Native Method Stacks
Native Method
: 다른 프로그래밍 언어로 작성된 메소드 (자바로 구성된 코드만을 사용할 수 없는 시스템 자원이나 API이용시)Native Method Stacks
: 자바로 작성되지 않은 메소드를 다루는 영역- JNI가 실행
실행 엔진 Execution Engine
인터프리터 Interpreter
- 바이트 코드를 한 줄 씩 실행
JIT 컴파일러
- 인터프리터 효율을 높이기 위해, 반복되는 코드를 발견하면 JIT 컴파일러가 반복되는 코드를 모두 네이티브 코드로 바꿔놓고, 다음에 중복 사용할 때 네이티브 코드로 이미 컴파일링 된 코드를 사용한다.
- 프로그램 속도를 향상시키기 위함
GC
- 더 이상 참조되지 않는 객체를 모아서 정리한다.
JNI : Java Native Interface
- 자바 어플리케이션에서 C, C++, 어셈블리로 작성된 함수를 사용할 수 있는 방법 제공
- Native 키워드를 사용한 메소드 호출
Native Method Library
- C, C++로 작성된 라이브러리
reference