JVM 동작 방식
•
자바 가상 머신(JVM, Java Virtual Machine)은 자바에서만 사용되는 것이 아니라, kotlin과 scalar 에서도 JVM 동작 방식을 그대로 사용한다.
•
위 그림은 간략한 자바 어플리케이션의 구동절차를 나타낸 것으로, JVM은 빨간 박스 친 동작을 수행한다. 컴파일된 .class 파일을 특정한 방법으로 처리하여 프로그램을 실행시키는 것이다.
•
자바 어플리케이션의 구체적인 동작 과정은 다음과 같다.
1.
자바 프로그램을 실행하면 JVM이 OS로부터 메모리를 할당 받는다.
2.
자바 컴파일러(javac)가 .java의 자바 소스코드를 .class의 자바 바이트 코드로 컴파일 한다.
3.
클래스 로더(Class Loader)가 동적 로딩으로 필요한 클래스들을 로딩(loading) 및 링크하여 Runtime Data Area(실질적인 메모리를 할당받아 관리하는 영역)에 올린다.
4.
Runtime Data Area에 로딩된 바이트 코드는 Execution Engine을 통해 해석된다. 추가적으로 Garbage Collector와 Thread 동기화도 Execution Engine에 의해 수행된다.
JVM의 구조
•
JVM의 구성은 다음과 같다.
◦
클래스 로더(Class Loader)
◦
실행 엔진(Execution Engine)
▪
인터프리터(Interpreter)
▪
JIT 컴파일러(Just-in-Time Compiler)
▪
가비지 컬렉터(Garbage Collector)
◦
런타임 데이터 영역(Runtime Data Area)
▪
Method 영역
▪
Heap 영역
▪
Stack 영역
▪
PC Register
▪
Native Method Stack
◦
JNI - 네이티브 메서드 인터페이스(Native Method Interface)
◦
네이티브 메서드 라이브러리(Native Method Library)
클래스 로더(Class Loader)
•
클래스 로더는 JVM 내로 클래스 파일(.class)을 동적으로 로드하고, 로드된 바이트 코드들을 엮어서 Runtime Data Area에 배치하는 모듈이다.
•
클래스 파일을 메모리에 올리는 로딩 작업은 한 번에 모두 올리는게 아니라, 어플리케이션에서 필요로 할 때 동적으로 메모리에 적재한다.
•
Loaing(로드) : 클래스 파일을 가져와서 JVM의 메모리에 로드
•
Linking(링크) : 클래스 파일을 사용하기 위해 검증하는 과정
◦
Verifying(검증) : 읽어온 클래스가 JVM에 명세에 맞게 구성되어 있는지 검사
◦
Preparing(준비) : 클래스가 필요로하는 메모리를 할당
◦
Resolving(분석) : 클래스의 상수 풀 내 모든 심볼릭 레퍼런스를 다이렉트 레퍼런스로 변경
•
Initalization(초기화) : 클래스 변수(static 필드)들을 초기화
실행 엔진(Execution Engine)
•
실행 엔진은 클래스 로더에 의해 Runtime Data Area에 배치된 바이트 코드를 명령어 단위로 실행한다.
•
자바 바이트 코드(.class)는 기계가 이해할 수 있는 언어가 아니라 가상머신(JVM)이 이해할 수 있는 중간 레벨로 컴파일 된 코드이다. 이러한 바이트 코드를 인터프리터와 JIT 컴파일러 방식을 통해 실행한다.
•
인터프리터(Interpreter)
◦
바이트 코드 명령어를 하나씩 읽어서 해석하고 실행하는 방식으로, JVM의 바이트 코드는 기본적으로 인터프리터 방식으로 동작한다.
◦
다만 같은 메서드를 여러 번 호출하더라도, 매번 해석하고 수행하기 때문에 전체적인 속도가 느리다.
•
JIT 컴파일러(Just-In-Time Compiler)
◦
반복되는 코드를 발견하여 바이트 코드 전체를 컴파일 후 Native Code로 변경하고, 반복되는 메서드를 캐싱해두었다고 네이티브 코드로 직접 실행하는 방식이다.
◦
인터프리터의 단점을 보완한 방식으로 보다 빠르지만 Native 코드로 변환하는 비용이 있기 때문에, 인터프리터를 기본으로 사용하다가 일정 기준이 넘어가면 JIT 컴파일 방식으로 명령어를 실행한다.
•
가비지 컬렉터(Garbage Collector)
런타임 데이터 영역(Runtime Data Area)
•
런타임 데이터 영역은 JVM의 메모리 영역으로, 자바 어플리케이션을 실행할 때 사용되는 데이터를 로드하는 영역이다.
•
런타임 데이터 영역은 Method 영역, Stack 영역, Heap 영역, PC Register, Native Method Stacks로 나뉘어진다.
•
Method 영역, Stack 영역, Heap 영역
•
PC Register
◦
현재 실행 중인 JVM 주소를 가지고 있는 공간
◦
PC Register는 CPU Register와는 별개이고, 자바는 CPU에 직접 연산을 수행하지 않고 현재 작업하는 내용을 CPU에 제공한다. PC Register는 이를 위한 버퍼 공간이다.
◦
스레드가 시작될 때 생성되고, 스레드가 소멸될 때 해제된다.
•
Native Method Stack
◦
기계어나 자바 외의 언어로 작성된 네이티브 코드를 위한 메모리
◦
C나 C++ 등의 코드를 수행하기 위한 스택으로 native 메서드의 매개변수와 지역변수 등을 바이트 코드로 저장한다.
◦
JIT 컴파일러에 의해 변환된 Native 코드도 이 메모리 영역에서 실행된다.
◦
JNI와 연결되어 있어, native interface 호출 시 바이트 코드로 전환되어 Native Method Stack에 저장된다.
•
Method 영역과 Heap 영역은 모든 스레드가 공유한다.
•
Stack 영역과 PC Register, Native Method Stack은 각 스레드마다 개별 생성되고 서로 공유되지 않는다.
JNI
•
JNI(Java Native Interface)는 자바가 다른 언어로 만들어진 어플리케이션과 상호작용 할 수 있도록 인터페이스를 제공하는 프로그램이다.
•
JVM이 Native Method를 Natvie Method Stack에 로드하고 수행할 수 있도록 한다.
•
실질적으로 제대로 동작되는 언어는 C와 C++ 정도밖에 없다.
Native Method Library
•
C나 C++로 작성된 라이브러리를 말하는 것으로, 헤더가 필요하다면 JNI가 이 라이브러리를 로딩해 실행한다.