JVM이란 무엇인가?
JVM(Java Virtual Machine)은 자바를 실행하기 위한 가상기계로 프로그램을 실행하기 위해 물리적 머신과 유사한 머신을 소프트웨어로 구현한 것으로 자바 어플리케이션을 클래스 로더를 통해 읽어들여서 자바 API와 함께 실행하는 역할을 합니다.
Write once, run anywhere(한 번 작성하면 어디서든 실행된다.)
JAVA언어로 작성된 애플리케이션은 JVM을 거쳐서 운영체제와 상호작용하게 됩니다. JVM은 바이트코드(컴파일된 자바 코드)를 하드웨어/OS 환경에 알맞게 변경해주는 역할을 해주고, 이를 통해서 운영체제로부터 독립적으로 프로그램을 작성할 수 있는 장점이 있습니다.
단 JVM은 운영체제에 종속적이기 때문에 해당 운영체제에서 실행가능한 JVM이 필요합니다.
자바 프로그램 실행 과정
1. 프로그램이 실행되면 JVM은 OS로부터 프로그램이 필요로 하는 메모리를 할당받는다.
2. 자바 컴파일러(javac)가 자바 소스코드(.java)를 읽어들여 바이트코드(.class)로 변환시킨다.
3. Class Loader를 통해 class 파일들을 JVM으로 로딩한다.
4. 로딩된 class파일들은 Execution engine을 통해 해석된다.
5. 해석된 바이트 코드는 Runtime Data Area에 배치되어 실행된다.
실행과정 속에서 JVM은 스레드 동기화, GC의 관리작업을 수행한다.
JVM의 구조와 역할
클래스 로더(Class Loader)
자바는 컴파일 타임이 아닌 런타임에 클래스를 처음 참조할 때 해당 클래스를 로드하고 링크합니다. 이 때 클래스 파일을 동적으로 로드하고 링크를 통해 배치하는 작업을 하는 것이 클래스 로더입니다.
클래스 파일은 Loading -> Linking -> initialization 의 과정을 거쳐 클래스를 로드하고 링크하고 초기화합니다.
Loading : 클래스 파일을 가져와 JVM 메모리에 로드합니다.
Linking : 클래스 파일 사용을 위해 검증하는 과정
- Verify : 클래스가 자바 언어 명세 및 JVM 명세에 명시된 대로 잘 구성되어 있는지 검사합니다.
- Prepare : 클래스가 필요로 하는 메모리를 할당하고 클래스에 정의된 필드, 메서드, 인터페이스를 나타내는 데이터 구조 를 준비합니다.
- Resolve : 클래스 상수 풀 내 모든 심볼릭 레퍼런스를 다이렉트 레퍼런스로 변경합니다.
Initialization : 클래스 변수들을 적절한 값으로 초기화 합니다.
실행엔진(Execution Engine)
클래스 로더를 통해 JVM 내의 런타임 데이터 영역에 배치된 바이트 코드를 명령어 단위로 읽어서 실행하는 역할을 합니다.
자바 바이트 코드는 기계가 바로 수행할 수 있는 언어보다 비교적 인간이 보기 편한 형태로 기술되어 있습니다. 그래서 실행 엔진은 바이트 코드를 실제로 JVM 내부에서 아래의 두 가지 방법을 통해 기계가 실행할 수 있는 형태로 변경해 줍니다.
인터프리터 방식 : 바이트 코드 명령어를 하나씩 읽어 해석하고 실행합니다. 하나하나의 해석은 빠른 대신 인터프리팅 결과의 실행은 느리다는 단점이 있습니다.
JIT 방식 : 인터프리터 방식의 단점을 보완하기 위해 도입되었습니다. 인터프리터 방식으로 실행하다 적절한 시점에 바이트 코드 전체를 컴파일해 네이티브 코드로 변경하고 이후에는 더 이상 인터프리팅 하지 않고 네이티브 코드로 직접 실행합니다. (네이티브 코드는 캐시에 보관하기 때문에 한 번 컴파일된 코드는 계속 빠르게 수행되게 됩니다.)
컴파일러가 컴파일하는 과정은 바이트코드를 인터프리팅 하는 것보다 시간이 오래걸리므로 JVM은 내부적으로 메서드가 얼마나 자주 수행되는지 체크하고 일정 정도를 넘을 때에만 컴파일을 수행합니다.
GC(Garbage Collector) : 자바 가상 머신은 가비지 컬렉터를 이용해서 Heap 메모리 영역에 사용하지 않는 메모리를 자동으로 회수하는 역할을 해줍니다.
런타임 데이터 영역(Runtime Data Area)
런타임 데이터 영역은 JVM의 메모리 영역으로 자바 애플리케이션을 실행할 때 사용되는 데이터를 적재합니다.
JVM 스택 : 각 스레드마다 하나씩 존재하며 각종 형태의 변수, 임시 데이터, 스레드, 메소드 정보를 저장하기 위한 영역으로 메소드 호출 시마다 각각의 스택 프레임(메서드를 위한 공간으로 메서드의 매개변수, 지역변수 리턴값 등등이 담긴다.)이 생성됩니다.
PC Registers : 스레드가 시작될 때 생성되며 각 스레드마다 한개씩 존재합니다. 현재 수행 중인 JVM명령의 주소를 갖습니다.
Native method stack : 자바 프로그램이 컴파일되어 생성되는 바이트 코드가 아닌 실제 실행 가능한 기계어로 작성된 프로그램을 실행시킨 영역입니다.(JAVA가 아닌 다른 언어로 작성된 코드를 위한 공간 대부분 C/C++) JNI를 통해 바이트 코드로 전환하여 저장합니다.
Heap : 인스턴스 또는 객체를 저장하는 공간(가비지 컬렉터의 대상이 됩니다.)
Young Generation : 생명주기가 짧은 객체를 가비지 컬렉터 대상으로 하는 영역입니다.
- Eden : new를 통해 새로 생성된 객체가 위치합니다. GC 후 남은 객체들은 Survivor로 이동합니다.
- Survivor 0 , Survivor1 : 각 영역이 채워지면 남은 객체는 순차적으로 이동합니다. 0 -> 1
Old Generation : 생명주기가 긴 객체를 GC 대상으로 하는 영역
Method Area : 모든 스레드가 공유하는 영역으로 JVM이 시작될 때 생성되는 공간 바이트 코드를 처음 메모리에 올릴 때 초기화되는 대상을 저장하기 위한 메모리 공간입니다.
- Field info : 멤버변수 이름, 데이터 타입 접근제어자 정보
- Method info : 메소드 이름, 리턴타입, 매개변수 접근제어자 정보
- Type info : class인지 interface인지 여부 등등을 저장
Runtime Constant pool : 메서드 영역에 존재하는 영역으로 상수 자료형을 저장합니다. JVM이 Constant pool을 통해 해당 메소드나 필드의 실제 메모리 주소를 찾아 참조하고 중복을 막는 역할을 수행합니다.
출처
'JAVA' 카테고리의 다른 글
[JAVA] 래퍼 클래스(Wrapper Class)란? (0) | 2024.05.31 |
---|---|
[JAVA] ENUM 이란? (0) | 2024.05.28 |
[JAVA] String이 불변(immutable)인 이유? (0) | 2023.04.11 |
[JAVA]String, StringBuilder, StringBuffer (0) | 2023.04.08 |
[JAVA] 인터페이스 (0) | 2022.11.23 |