Java를 동작시키는데 필요한 JVM에 대해 알아보겠습니다.
JVM은 다른 프로그램을 실행시키는 데 목적이 있습니다.
기능으로는 자바 프로그램이 어떤 기기나 OS에서도 실행될 수 있도록 하는데 있습니다.
또한 프로그램 메모리를 관리하고 최적화 하는데 있습니다.
간단한 요약
Java는 OS에 독립적인 특성을 가집니다. JVM을 통해 구현되기 때문입니다.
JVM의 동작원리는 다음과 같습니다.
동작순서를 개발자 입장에서 생각해보겠습니다.
1. 개발자가 자바 소스코드를 작성하고 실행해보겠습니다.
2. 자바 컴파일러가 Java Source파일을 컴파일합니다.
(이때, 자바 컴파일러는 자바를 설치하면 /bin 폴더에 있는 javac.exe라는 실행 파일 형태로 설치됩니다.)
Content
{file name}.java 파일을 {file name}.class인 자바 바이트 코드로 바꿔주는 작업입니다.
이러한 자바 바이트 코드의 확장자가 .class인 것입니다.
이때 코드는 컴퓨터가 읽을 수 있는 언어가 아닌 JVM이 읽을 수 있는 1Byte 크기의 code와 피연산자로 구성된 코드입니다.
자바 바이트 코드는 자바 가상 머신만 설치되어 있으면, 어떤 운영체제에서라도 실행될수 있는 특징이 있습니다.
3. JVM의 클래스 로더에게 해당 코드를 전달합니다.
4. 클래스 로더는 동적 로딩을 통해 필요한 클래스들을 로딩 및 링크하여 런타임 데이터 영역, 즉 JVM의 메모리에 올립니다.
5. Execution Engine은 JVM의 메모리에 올라온 바이트 코드들을 명령어 단위로 하나씩 가져와서 실행합니다.
6. 인터프리터 또는 JIT 컴파일러에 의해 실행할 수 있는 코드로 변경됩니다.
JVM의 구조
JVM의 내부 구조는 다음과 같습니다.
각각에 대한 세부사항을 좀 더 자세하게 알아보겠습니다.
1. 클래스 로더 시스템 - 로딩
자바 클래스는 한번에 로더되지 않고 애플리케이션에서 필요로 할때, 하나씩 로드된니다.
JRE 의 일부인 클래스 로더는 런타임에 클래스를 동적으로 JVM에 로드하는 역할을 수행하는 모듈입니다.
자바의 클래스들은 자바 프로세스가 새로 초기화되면 클래스 로더가 차례차례 로딩되며 동작합니다.
동작 순서는 다음과 같습니다.
1. JVM의 메소드 영역에 클래스가 로드되어 있는지 확인한다. 만일 로드되어 있는 경우 해당 클래스를 사용한다.
2. 메소드 영역에 클래스가 로드되어 있지 않을 경우, 시스템 클래스 로더에 클래스 로드를 요청한다.
3. 시스템 클래스 로더는 확장 클래스 로더에 요청을 위임한다.
4. 확장 클래스 로더는 부트스트랩 클래스 로더에 요청을 위임한다.
5. 부트스트랩 클래스 로더는 부트스트랩 Classpath(JDK/JRE/LIB)에 해당 클래스가 있는지 확인한다.
클래스가 존재하지 않는 경우 확장 클래스 로더에게 요청을 넘긴다.
6. 확장 클래스 로더는 확장 Classpath(JDK/JRE/LIB/EXT)에 해당 클래스가 있는지 확인한다.
클래스가 존재하지 않을 경우 시스템 클래스 로더에게 요청을 넘긴다.
7. 시스템 클래스 로더는 시스템 Classpath에 해당 클래스가 있는지 확인한다.
클래스가 존재하지 않는 경우 ClassNotFoundException을 발생시킨다.
부트스트랩 클래스 로더(Bootstrap Class Loader)
JVM 시작 시 가장 최초로 실행되는 클래스 로더입니다.
부트스트랩 클래스 로더는 자바 클래스를 로드하는 것이 아닌, 자바 클래스를 로드할 수 있는 자바 자체의 클래스 로더와 최소한의 자바 클래스(java.lang.Object, Class, ClassLoader)만을 로드합니다.
Java 8
jre/lib/rt.jar 및 기타 핵심 라이브러리와 같은 JDK의 내부 클래스를 로드합니다.
Java 9 이후
더 이상 /re.jar이 존재하지 않으며, /lib 내에 모듈화되어 포함됐다. 이제는 정확하게 ClassLoader 내 최상위 클래스들만 로드합니다.
확장 클래스 로더(Extension Class Loader)
확장 자바 클래스들을 로드한다. java.ext.dirs 환경 변수에 설정된 디렉토리의 클래스 파일을 로드하고,
이 값이 설정되어 있지 않은 경우 ${JAVA_HOME}/jre/lib/ext 에 있는 클래스 파일을 로드합니다.
시스템 클래스 로더(System Class Loader)
자바 프로그램 실행 시 지정한 Classpath에 있는 클래스 파일 혹은 jar에 속한 클래스들을 로드합니다.
쉽게 말하자면, 우리가 만든 .class 확장자 파일을 로드합니다.
-> 만약 class를 찾지 못하면 ClassNotFoundException이 발생합니다.
2. 클래스 로더 시스템 - 링크
검증 : 자바 언어 명세 및 JVM 명세에 명시된 대로 구성되어 있는지 확인합니다. (유효성 체크)
준비 : 클래스가 필요로 하는 메모리를 할당합니다. 클래스 변수(static 변수)와 기본값에 필요한 메모리를 준비
분석 : 클래스의 상수 풀 내 모든 심볼릭 레퍼런스를 다이렉트 레퍼런스로 변경합니다.
public class Application {
Hello hello = new Hello();
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
분석 단계에서 Hello class의 경우 new Hello()를 정의한다고 바로 실제 레퍼런스를 가리키지 않습니다.
힙에 있는 인스턴스를 가리켜서 실제 사용할 수 있도록 만드는 것을 분석 단계에서 실행합니다.
3. 클래스 로더 시스템 - 초기화
클래스 변수들을 적절한 값으로 초기화 합니다.
4. 실행 엔진 (Execution Engine)
실행 엔진은 클래스 로더를 통해 런타임 데이터 영역에 배치된 바이트 코드를 명령어 단위로 읽어서 실행합니다.
처리 방법은 인터프리터(Interpreter)과 JIT(Just In Time) 2가지가 있습니다.
인터프리터와 JIT의 차이점
Java는 다른 언어들에 비해서 느리다는 평가를 많이 받아왔다고 합니다. 이유는 자바의 특징인 인터프리터를 하는 과정과 그 과정에서 컴파일을 한번 진행하기 때문에 발생합니다.
게다가 인터프리터의 경우 소스코드를 런타임 시에 한줄 한줄 읽어 들여야 하는 방식이기 때문에 속도가 느린점이 있습니다.
JIT 컴파일러는 Interpreter의 단점을 보완하기 위해 도입된 방식으로 반복되는 코드를 발견하여 바이트 코드 전체를 컴파일 하여 Native code로 변경하고, 이후에는 메서드를 더 이상 인터프리팅하지 않고 캐싱해 두었다가 네이티브 코드로 직접 실행하는 방식입니다.
5. 런타임 데이터 영역(Runtime Data Area)
런타임 데이터 영역은 실제 클래스 파일이 적재되는 곳으로 JVM이 OS로부터 자바 프로그램 실행을 위한 데이터와 명령어를 저장하기 위해 할당받는 메모리 공간입니다.
JVM단위와 Thread단위로 나뉘어 집니다.
JVM단위
JVM이 시작할때 단 하나만 생성되고, 모든 스레드간 공유를 합니다. JVM이 종료될때 비워지는 데이터입니다.
Heap과 Method Area는 각 스레드가 메모리를 공유합니다.
Heap, Method Area, Runtime Constant Pool이 해당됩니다.
Heap
힙 영역은 클래스 인스턴스와 Array객체들처럼 생명주기가 긴 데이터들이 저장되는 공간입니다.
Heap에 대한 메모리 관리는 GC(가비지 컬렉터)에 의해 관리됩니다.
가바지 컬렉션은 아래 글을 참조해 주세요
2024.01.07 - [기술 스텍/Java] - 가비지 컬렉션이란(JAVA)
Method Area
메서드 영역은 클래스 로더에 의해 로드된 클래스 정보를 처음 메모리 공간에 올릴 때, 초기화 되는 대상을 저장하는 공간입니다.
Runtime Constant Pool
메서드 영역에 클래스 정보가 로드될 때 생성됩니다.
런타임에 가져올 메서드와 필드의 참조등 다양한 상수(리터럴 상수)들이 저장되는 공간입니다.
메서드나 필드를 참조할 때, 필드의 실제 메모리 상 주소를 참조해 중복을 막아주는 역할을 합니다.
Tread 단위
스레드가 실행될 때 생성되는 공간입니다.
PC Register, Stack 메모리, Native Method Stack이 해당되는 공간입니다.
PC Register
스레드가 생성될 때마다 생기는 공간으로 스레드의 실행중인 명령을 저장하는 역할을 의미합니다.
Stack
JVM Frame을 저장하는 공간입니다. 메모리의 크기는 고정크기와 가변크기가 있으며, 페이지네이션과 세그먼트 등에 의해 결정됩니다.
JVM Frame이란?
함수 실행시 생성되며, 데이터나 부분적인 결과값을 저장합니다.
종류
1. Local Variables
scope 내 지역 변수들, 지역 변수는 array형태로 저장됩니다.
2. Operand Stack
연산을 위한 작업 공간입니다.
컴파일 타임에 결정되며 생성될때에는 비어있으나, 계산 과정에서 사용됩니다.
3. Runtime Constant Pool Reference
컴파일 시 확인해야 하는 상수들부터 런타임 시 확인해야할 메서드, 필드 값까지 다양한 상수를 담아두는 테이블
런타임 상수 풀의 참조를 가집니다.
Native Method Stack
Java 외 언어로 구성된 네이티브 코드를 위한 코드입니다.
Method Area
java 8 이전에는 PermGen에 할당해서 저장했으며,
java 8 이후부터 PermGen이 제거되고 Method Areasms Native Heap에 할당되기 시작했습니다.
프로그램 실행 중 JVM이 클래스 파일을 분석합니다. 그리고 클래스의 인스턴스 변수, 메서드 코드 등을 Method Area에 저장합니다.
즉, class에 대한 direct reference 값을 가지게 됩니다.
'기술 스텍 > Java' 카테고리의 다른 글
직렬화란? (0) | 2024.02.03 |
---|---|
Call By Value와 Call By Reference 차이점 분석 (0) | 2024.01.20 |
String과 String Builder, String Buffer 차이점 (1) | 2024.01.11 |
고유 락이란? -Java (0) | 2024.01.08 |
[Java] 가비지 컬렉션이란? (0) | 2024.01.07 |