JVM / JRE / JDK
JVM (Java Virtual Machine)
자바 프로그램을 운영체제와 독립적으로 실행할 수 있게 해주는 환경
가비지 컬렉션(Garbage Collection)으로 프로그램의 메모리를 자동으로 관리
JRE (Java Runtime Environment)
자바 프로그램을 실행하기 위한 환경
- Java Class Library: java.io, java.util, java.thread 처럼 작동에 필수적인 구성요소를 담은 라이브러리
JDK (Java Develop Environment)
자바 프로그램의 개발을 위한 환경
- javac: 자바 소스코드(.java)를 자바 바이트코드(.class)로 컴파일
Java 실행 프로세스
Java 프로그램 실행 흐름
- 자바 소스코드(.java)를 컴파일러를 통해 JVM이 읽을 수 있는 형태인 자바 바이트코드(.class)로 변환
- 자바 바이트코드를 Class Loader를 통해 JVM 메모리 영역인 Runtime Data Area에 로드
- Execution Engine이 Runtime Data Area 영역에 올라온 바이트코드를 컴퓨터가 읽을 수 있는 바이너리코드인 기계어로 변환하며 프로그램 수행
Class Loader
자바 소스코드는 .java 형식으로 작성되어 컴파일러를 통해 자바 바이트코드인 .class 형태로 변환된다.
이렇게 변환된 바이트코드를 JVM의 메모리 영역인 Runtime Data Area로 로드하는 것이 Class Loader의 역할이다.
이 때 바이트코드를 로드하는 시점이 프로그램 실행을 시작하는 시점이 아니라 런타임에 각 클래스가 처음으로 참조되는 시점이기 때문에 동적 로드(Dynamic Load) 한다고 말한다.
한 번 클래스 데이터를 메모리에 로드한다면 프로그램이 종료될 때까지 메모리에서 내려가지 않는다. 그렇기에 메모리를 가능한 적게 사용하기 위해서 이런 동적 로드 형태를 가지고 있다고 한다.
Runtime Data Area
JVM이 프로그램을 실행하기 위해 OS로 부터 별도로 할당받은 메모리 공간
모든 스레드가 공유하는 영역이 있고 각 스레드 별로 분리되는 영역이 있다
모든 Thread가 공유하는 영역
- Heap
- new 키워드로 생성되는 객체와 배열 등이 저장되는 영역
- 메모리 최적화를 위해 GC가 관리하는 영역
- String Constant Pool을 포함한다.
- Method Area (=Static Area)
- 클래스의 필드와 메소드, 생성자의 정보, static 변수, 메소드의 바이트코드 등을 보관
- 이러한 클래스의 메타데이터로 Heap 영역에 객체를 생성한다.
- 한 번 로드된 클래스는 프로그램이 종료될 때까지 저장된다.
- Runtime Constant Pool을 포함한다.
- 클래스 생성시 참조해야할 정보를 상수로 가지고있는 영역
- 각 클래스/인터페이스 마다 별도의 Constant Pool 테이블을 갖는다.
- 클래스의 필드와 메소드, 생성자의 정보, static 변수, 메소드의 바이트코드 등을 보관
Thread별로 생성되는 영역
- JVM Stack Area
- Stack Frame을 저장하는 스택
- 메소드가 수행될 때마다 하나의 스택 프레임이 생성되어 JVM 스택에 추가되고, 메소드가 종료되면 스택 프레임이 제거된다.
- 각 스택 프레임은 지역변수, 매개변수, 현재 실행중인 메서드가 속한 클래스의 Runtime Constant Pool에 대한 레퍼런스를 갖는다.
- 지역변수가 원시타입이라면 스택 영역에 직접 값을 가지고, 참조타입이라면 객체의 주소 값을 가진다.
- PC register
- 현재 수행중인 JVM 명령의 주소와 명령을 저장한다.
- Native Method Stack
- 자바 외의 언어(C, C++)로 작성된 코드를 위한 스택
- JNI를 통해 사용된다.
Execution Engine
JVM의 Runtime Data Area에 로딩된 자바 바이트코드를 명령어 단위로 읽어서 실행하는 역할
자바의 바이트코드(.class)는 컴퓨터가 읽을 수 있는 기계어가 아니라 JVM이 읽을 수 있는 형태이므로, 프로그램을 실행하기 위해 기계어로 변환하는 과정이 필요하다.
이 과정을 인터프리터와 JIT 컴파일러 두 가지 방식으로 진행한다.
인터프리터(Interpreter)
“인터프리터 언어”의 인터프리터와 같은 의미라고 볼 수 있다.
바이트코드 명령어를 한 줄 씩 읽고 바로 실행하는 행위의 반복으로 프로그램을 수행한다. 따라서 같은 메소드가 여러 번 호출된다면 매번 다시 해석하는 과정을 거치기때문에 전체적인 수행 속도가 느리다.
JIT 컴파일러(Just-In-Time Complier)
인터프리터가 같은 메소드를 여러 번 해석하는 단점을 보완하기 위해 나온 방식으로 인터프리터와 컴파일러의 장점을 혼합했다.
일반적인 실행은 인터프리터처럼 한 줄 씩 읽고 실행하지만, 일정 기준 이상으로 반복되는 코드를 발견하면 바이트 코드 전체를 한 번에 컴파일하여 캐싱해 빠르게 실행만 할수있다.
가비지 컬렉션 (Garbage Collection)
Heap 영역의 데이터 중 더이상 참조되지 않는 의미없는 객체를 주기적으로 정리하는 역할을 한다.