Java로 개발을 한다면 Java가 어떻게 실행되는지는 알아야하지 않을까,, 라는 생각으로 정리해보는 JVM의 구조
1. JVM이란?
JVM이란 Java Virtual Machine의 약자로, Java Byte Code를 실행하는 주체이다.
JVM을 이용하여 (1) Java Byte Code를 OS에 맞게 해석할 수 있고, (2) GC로 자동으로 메모리를 관리 할 수 있다.
[참고]
Java 개발과 실행을 위한 주요 구성요소에는 JDK, JRE, JVM이 있다.
- JDK (Java Development Kit) : Java 개발에 필요한 도구 모음으로 JRE와 컴파일러, 디버거와 같은 개발에 필요한 도구들로 구성된다.
- JRE (Java Runtime Environment) : Java 실행 환경으로, JVM과 Java API로 구성된다.
- JVM : Java Byte Code를 실행하는 주체
2. Java의 실행 과정
JVM 동작 과정을 포함한 Java의 실행 과정은 다음과 같다.
1. 소스 코드(.java)를 실행하면 JVM은 OS로부터 메모리를 할당받는다.
2. 자바 컴파일러가 javac 명령어로 바이트 코드(.class)로 컴파일한다.
3. JVM의 Class Loader를 통해 바이트 코드를 JVM Runtime Data Area로 로드한다.
4. 이를 Execution Engine에서 기계어로 해석한다.
5. 기계어를 Runtime Data Area의 적절한 영역에 배치하여 수행시킨다.
6. 실행하며 Execution Engine에 의해 GC와 스레드 동기화가 이루어진다.
3. JVM의 구조
JVM은 Class Loader, Runtime Data Area, Execution Engine, GC 그리고 JNI, Native Method Library로 이루어져 있다.
각각에 대해 알아보자.
ClassLoader
ClassLoader는 Java Byte Code를 동적으로 로드하여 Runtime Data Area에 배치하는 역할을 한다.
ClassLoader에 의한 로딩 과정은 Loading → Linking → Initialization의 3단계로 이루어진다.
Loading
Java Byte Code를 Runtime Data Area에 로드하는 작업이다.
보통 static main()에서 시작되며, 이미 load 된 클래스는 unload 될 수 없다.
ClassLoader는 Bootstrap Class Loader, Extension ClassLoader, System ClassLoader, User-Defined ClassLoader의 계층으로 이루어져 있다.
로딩 과정에서 상위 클래스로더에 요청을 위임하고, 상위 클래스로더에서 하위 클래스로더로 내려오며 클래스를 찾게 된다.
최하위 클래스로더도 클래스를 찾지 못하는 경우 ClassNotFoundException이 발생한다.
Linking
Byte Code를 검증하는 단계이다. 이는 검증 → 준비 → 분석의 3단계로 이루어진다.
검증 단계는 Byte Code의 유효성과 안정성을 확인하는 단계이다. JVM 규격을 기준으로 확인한다.
준비 단계에서는 JVM에 의한 데이터 구조나 클래스 변수들에 메모리를 할당하고 기본값으로 초기화한다.
분석 단계에서는 Symbolic Reference를 Direct Reference로 바꾸어 실제 객체의 주소를 참조시킨다.
Initialization
인터페이스와 클래스의 초기화 로직이 실행되는 단계이다. Linking의 준비단계에서 초기화한 static 변수를 개발자가 지정한 값으로 정의한다.
Runtime Data Area
JVM이 실행될 때 OS로부터 할당 받은 메모리 영역이다.
모든 스레드가 공유하는 메서드 영역, 힙 영역, 그리고 스레드마다 하나씩 생성되는 스택 영역, PC 레지스터 네이티브 메서드 스택이 있다.
Method 영역
메서드 영역은 클래스에 대한 정보가 저장되는 영역이다.
JVM이 시작될 때 생성되며, 프로그램이 종료될 때까지 저장된다.
Heap 영역
인스턴스가 생성되는 영역으로 GC의 대상이다.
효율적인 GC를 위해 Young Generation과 Old Generation으로 나뉜다.
Stack 영역
메서드의 작업에 필요한 메모리 공간을 제공한다.
지역 변수, 파라미터, 리턴 값, 임시 값 등이 생성되며, 메서드가 종료되면 메모리를 반환한다.
PC Register
스레드 시작 시에 생성되며, 현재 수행되고 있는 JVM 명령어의 주소를 저장한다.
이를 통해 순차적으로 명령어를 실행할 수 있다.
Native Method Stack
Java Byte Code가 아닌 실제 실행할 수 있는 기계어로 작성된 코드를 실행하는 영역이다.
Execution Engine
Java Byte Code를 기계어로 해석하여 실행하는 영역이다.
기본적으로 인터프리팅 방식을 사용하며, 일정 기준이 넘어가면 JIT 컴파일러 방식을 이용한다.
인터프리팅 방식은 한 줄 한 줄 해석하여 실행하는 방식이다. 반복된 코드도 다시 해석하기 때문에 속도가 느리다.
반면, JIT 컴파일러 방식은 실행할 때에 코드를 기계어로 바꾸고 캐싱해 두었다가 재사용하는 방식이다. 인터프리팅에 비해 속도가 빠르다.
GC (Garbage Collection)
자동으로 힙 메모리를 관리해주는 영역으로, GC가 있기 때문에 개발자가 직접 메모리를 관리하지 않아도 된다.
힙 메모리에서 참조되지 않는 객체를 탐색하여 제거한다.
GC가 진행될 때에는 GC를 수행하는 스레드 외의 스레드는 실행 중지 된다.(Stop-the-World)
JNI (Java Native Interface)
Java 언어와 네이티브 언어 간의 상호 작용을 위한 인터페이스이다.
Native Method Library
C나 C++로된 라이브러리로, Java 프로그램에서 네이티브 메서드를 호출할 수 있도록 제공된다.
'Language > Java' 카테고리의 다른 글
[Java] BigInteger 사용하기 (0) | 2024.08.22 |
---|---|
[Java] Java 코딩테스트 기본 메소드 (0) | 2024.03.28 |
[Java] String compareTo() 메소드 (0) | 2024.02.25 |
[Java] 빌드 도구 Gradle (0) | 2024.01.22 |
java 오류 메시지 모음 (0) | 2023.02.10 |