JVM이란?
JVM은 자바 가상 머신으로 자바 바이트 코드를 실행할 수 있는 주체다.
운영체제 위에서 동작하는 프로세스로 자바 코드를 컴파일해서 얻은 바이트 코드를 해당 운영체제에 맞는 기계어로 바꿔서 실행시켜주는 역할을 한다.
자바 가상머신은 크게 Class Loader(클래스 로더), Execution Engine(실행 엔진), Garbage Collector(GC), Runtime Data Area로 나뉜다.
참고로 가상 머신이란, 프로그램 실행을 위해 물리적 머신과 유사한 머신을 소프트웨어로 구현한 것이다.
1. Class Loader (클래스 로더)
.class 파일을 로드하고 링크를 통해 배치하는 작업을 수행하는 모듈이다. Runtime시에 동적으로 클래스를 로드한다.
.jar 파일 내 저장된 클래스들을 JVM에 탑재하고 사용하지 않는 클래스들은 메모리에서 삭제한다. 자바는 동적 로딩을 지원한다. 따라서 컴파일 타임에 모든 클래스 로딩되지 않고 필요한 시점에 해당 클래스를 실행하고 로딩한다. 그 역할을 클래스 로더가 한다.
2. Execution Engine (실행 엔진)
실행 엔진은 클래스 로더에 의해 자바 바이트 코드로 로딩된 클래스들을 기계어로 변환시켜 실행한다.
실행 엔진은 기계어로 변환할 때 인터프리터 방식과 JIT 방식을 사용한다.
🔍 Interpreter (인터프리터)
실행 엔진은 자바 바이트 코드를 명령어 단위로 읽어서 실행한다. 이 방식의 단점은 속도가 느리다는 것이다.
🔍 JIT (Just - In - Time)
인터프리터 방식의 단점을 보완하기 위해 도입된 JIT 컴파일러다. 인터프리터 방식으로 실행하다가 적절한 시점에 바이트 코드를 전체 컴파일하여 네이티브 코드로 변경하고 이후에 더는 인터프리팅하지 않고 네이티브 코드로 실행하는 방식이다. 네이티브 코드를 캐시에 보관하기 때문에 한 번 컴파일된 코드는 빠르게 수행할 수 있다. 물론 JIT 컴파일러가 컴파일 과정은 바이트 코드를 인터프리팅하는 것보다 오래 걸리기 때문에 한 번만 실행되는 코드라면 컴파일하지 않고 인터프리팅하는 것이 유리하다. 따라서 JVM은 해당 메서드가 얼마나 자주 수행되는지 체크하고 JIT 컴파일러를 사용할지 말지 결정한다.
🔍 Garbage Collector (GC)
GC를 수행하는 모듈이다. 각 쓰레드에 존재한다.
4. Runtime Data Area
JVM이 OS에게 할당 받은 메모리를 의미한다.
Runtime Data Area는 각각의 목적에 따라 PC Register / JVM Stack / Native Method Stack / Heap / Method Area로 5개의 영역으로 나뉜다.
PC Register, JVM Stack(Stack Area), Native Method Stack은 각 Thread 별로 생성이 되지만, Heap과 Method Area는 모든 Thread와 공유한다.
🔍 PC Register (PC 레지스터)
Thread가 어떤 부분을 어떤 명령으로 실행해야할 지에 대한 기록을 하는 부분으로 현재 수행중인 JVM 명령의 주소를 갖는다.
Thread가 시작될 때 생성되며 쓰레드마다 하나씩 존재한다.
JVM은 Stack-Base 방식으로 작동한다. JVM은 CPU에 직접 명령하지 않고 Stack에서 피연산자를 뽑아내 이를 별도의 메모리 공간에 저장한다. 이 메모리 공간을 PC Register라고 한다.
🔍 JVM 스택 영역 (Stack Area)
지역변수, 메서드의 매개변수, 임시적으로 사용되는 변수, 메서드의 정보가 저장된다.
- 프로그램 실행 과정에서 임시로 할당 되었다가 메서드를 빠져나가면 바로 소멸되는 특성의 데이터를 저장한다.
- 지역 변수, 파라미터, 리턴 값, 연산에서 사용되는 임시 값들이 생성되는 영역이다.
🔍 Native Method Stack
자바 외 언어로 작성된 네이티브 코드를 위한 메모리 영역이다.
🔍 Method Area (= class area = static area)
클래스 멤버 변수의 이름, 데이터 타입, 상수, static 변수, final class 변수 등이 생성되는 영역이다.
JVM이 시작될 때 생성되는 공간으로 바이트코드가 이 영역에 저장된다.
🔍 Heap
동적으로 생성된 객체가 저장되는 영역으로 GC의 대상이 된다.
new 연산자로 생성된 인스턴스, 배열 등이 여기에 저장된다.
Heap은 세 부분으로 나눌 수 있다.
💡 Permanent Generation
생성된 객체들의 주소 값이 저장된 공간이다. 클래스 로더에 의해 class, method 등에 대한 meta 정보가 저장된다. reflection을 사용할 때 사용된다.
💡 New / Young 영역
Eden : 객체가 최초로 생성되는 공간
Servivor 0 / 1 : Eden에서 참조되는 객체들이 저장되는 공간
💡 Old 영역
New area에서 일정 시간 참조되고 있어서 살아남은 객체들이 저장된 공간이다.
즉, GC가 여러 번 발생했는데도 살아남은 객체들이다.
5. GC
🔍 GC 알고리즘 - Mark And Sweep
Root Space를 기점으로 시작해서 그래프 순회를 통해 연결된 객체들을 찾아서 마킹한다. 마킹이 끝나고 마킹이 없는 객체들을 제거한다. 제거 후, 데이터를 정렬하여 메모리 파편화를 막는다. 이를 Compaction이라고 한다.
참고로 Mark And Sweep에서 Compaction 과정은 필수 과정이 아니다.
✔ GC 호출 시기
Eden 영역의 메모리가 가득차면 (Minor) GC가 발생하고 Mark And Sweep 과정이 일어난다. Eden 영역에서 살아 남은 데이터들은 Servivor 영역으로 넘어간다.
먼저 Eden 영역에서 Servivor 0 으로 넘어간다. Servivior 0 메모리가 꽉차면 GC가 발생하고 Mark And Sweep 과정이 일어난다. 그리고 Servivor 0에서 살아남은 데이터들은 Servivor 1 영역으로 넘어가고 이동한 객체는 Age 값이 증가한다. 또, Servivor 1 영역에 메모리가 가득 차면 GC가 발생하고 Mark And Sweep 과정이 일어난다. 그리고 Servivor 1에서 살아남은 데이터들은 Servivor 0 영역으로 넘어가고 이동한 객체는 Age 값이 증가한다. 즉, Servivor의 0 영역 또는 1 영역중 한 곳은 무조건 비워진 상태를 유지한다. 그리고 특정 Age 값을 넘어가면 Old Generation 영역으로 넘어간다.
Old Generation 영역이 꽉차면 또 (Major) GC가 발생하고 Mark And Sweep 과정이 일어난다. 이 과정을 무한 반복한다.
GC가 수행하는 구간을 나눈 이유는 대부분 객체의 수명이 짧기 때문에 메모리 전체를 탐색하는 것 보다 메모리의 특정 부분을 중점으로 탐색하면 더 효율적이기 때문이다.
GC가 호출되면 GC를 수행하는 쓰레드를 제외한 모든 쓰레드가 동작을 멈춘다. 이것을 'stop-the-world'라고 한다.
GC는
✔ GC 종류
① Serial GC
하나의 쓰레드로 GC를 실행한다.
stop-the-world 시간이 길다.
② Parallel GC
여러 개의 쓰레드로 GC를 실행한다.
멀티코어 환경에서 사용한다.
Java 8 의 기본 GC 방식이다.
③ CMS GC
stop-the-world 시간을 최소화하기 위해 고안됐다.
GC 작업을 애플리케이션과 동시에 실행한다.
그러나 CG 쓰레드 수행 시간이 길어서 CPU 리소스를 많이 잡아먹고 메모리 파편화가 일어나는 단점이 존재한다.
G1 GC 등장에 따라 Deprecated 됐다.
④ G1 GC
G1은 Garbage First의 줄임말이다.
G1 GC는 Heap을 일정 크기의 Region으로 잘게 나눠서 어떤 영역은 Young Generation 어떤 영역은 Old Generation으로 활용한다.
전체 Heap이 아닌 Region 단위로 탐색한다.
Java 9 이상부터 기본 GC 방식이다.
👀 참고 자료
https://asfirstalways.tistory.com/158
https://parkadd.tistory.com/20
https://milkcaramel66.tistory.com/9
https://www.holaxprogramming.com/2013/07/16/java-jvm-runtime-data-area/
'[자바] > 자바' 카테고리의 다른 글
[자바] 자바 코드 스타일 컨벤션 (0) | 2022.05.14 |
---|---|
[Java] Optional<T> 클래스 (0) | 2022.02.07 |
[Java] 메서드 참조 (Method References) (0) | 2022.02.04 |
[Java] 함수형 인터페이스 (0) | 2022.02.03 |
[Java] 내부 클래스, 네스티드(Nested) 클래스, 익명 클래스 (0) | 2022.01.27 |