JVM에 관하여
시리즈를 기반으로, 보충하거나 정리하여 글을 작성하였다.Java Development Kit
자바를 활용하여 프로그램을 개발할 때 필요한 도구들의 모음
구성요소
JRE
컴파일러
xx.java
➔ xx.class
로 컴파일디버거
다양한 JDK 버전이 있다.
Oracle JDK
open JDK
Java Runtime Environment
JVM 위에서 프로그램이 원활하게 실행될 수 있는 환경 제공
.class
파일에 작성된 코드만으로는 부족하다.
필수적인 라이브러리들이 필요하다.
구성요소
JVM
Java Class Libraies
필수적인 라이브러리
Class Loader
Java Virtual Machine
자바로 작성된 프로그램 (.class
파일) 이 돌아가도록 만들어주는 프로그램
어느 기기, 운영체제와 상관없이 자바 프로그램은 JVM 위에서 실행 가능하다.
클래스 파일 (.class
) 들을 찾아 JVM의 메모리에 탑재한다.
Loading, Linking, Initialization 을 수행한다.
필요한 클래스 파일들을 찾아 탑재한다.
크게 3개의 계층으로 나뉜다.
Bootstrap ClassLoader
모든 ClassLoader 의 부모
JVM을 구동시키기 위한 가장 필수적인 라이브러리들을 탑재
JDK/JRE/LIB
에 위치 (ex, rt.jar
)운영체제에 맞게 네이티브 코드로 작성되어 있다.
Extensions ClassLoader
JDK/JRE/LIB/EXT
에 위치Application ClassLoader
Delegation Model (위임 모델)
클래스를 찾으라는 요청이 있을 때마다
ClassLoader Sub-System
은 Application ClassLoader
에게 위임하고
Application ClassLoader
은 Extensions ClassLoader
에게 위임하고
Extensions ClassLoader
은 Bootstrap ClassLoader
에게 위임한다.
Bootstrap ClassLoader
이 요청받은 클래스를 찾지 못하면 Extensions ClassLoader
에게 제어권을 돌려주고
Extensions ClassLoader
이 요청받은 클래스를 찾지 못하면 Application ClassLoader
에게 제어권을 돌려주고
Application ClassLoader
이 요청받은 클래스를 찾지 못하면 ClassNotFoundException
예외 발생
Uniqueness Property
탑재된 클래스들은 중복이 없다.
항상 상향 위임을 시도하기 때문에, 상위 ClassLoader 가 클래스를 찾지 못한 경우에만
현재 ClassLoader 가 클래스를 찾는다.
Visibility
하위 ClassLoader 가 로딩한 클래스는
상위 ClassLoader 가 로딩한 클래스를 볼 수 있다.
반대는 불가능하다.
이를 통해 클래스의 계층을 구분지어 관리할 수 있다.
ClassNotFoundException
프로그램 정상 수행
User.class
클래스 파일 제거
ClassNotFoundException
예외 발생
로드된 클래스 파일들을 검증한다.
클래스 파일들을 사용할 수 있게 준비한다.
크게 3가지 단계로 이루어져 잇다.
Verification
Preparation
Resolution
클래스 파일의 코드를 읽는다.
코드에서 지정한 값들로 필드들을 초기화 및 초기화 메서드를 실행한다.
멀티 쓰레딩으로 동작하므로 동시성을 고려해야 한다.
static block 을 실행한다
JVM 이 프로그램을 수행하기 위해 OS로부터 할당받는 메모리
구성요소
Method Area (Metaspace)
Heap
Java Stacks
PC registers
Native Method Stacks
클래스 구조, 메서드 데이터, 생성자 필드 데이터, 인터페이스, static 변수등을 저장
Runtime Constant Pool 이 할당된다.
클래스마다 생성되는 상수 풀
클래스가 사용하는 상수 값들을 저장
숫자 리터럴 같은 컴파일타임 상수
+ 메서드와 필드 참조같은 런타임 상수
JVM 당 하나만 생성
모든 쓰레드가 Method Area 공유
구동 시작시에 생성되고, 종료시까지 유지된다.
JVM의 다른 메모리 영역에서 해당 정보에 대한 요청이 오면, 해당 정보의 실제 물리 메모리 주소를 반환
Metaspace 는 Heap 공간이 아니다.
실제 데이터를 가진 인스턴스, 배열등을 저장
문자열 정보를 가진 String Constant Pool 이 할당된다.
JVM 당 하나만 생성된다.
Heap 영역의 데이터는 Java Stack 영역에서 참조되어 쓰레드간 공유된다.
Thread Safe 하지 않다.
Heap 이 가득차면 OutOfMemoryError
발생
Heap 에서 참조되지 않는 인스턴스와 배열은 GC의 주 대상이다.
- JVM Native
- 컴퓨터의 실제 물리적인 메모리 영역
- JVM 자체와 JVM이 실행되는 환경에서 사용되는 네이티브 라이브러리, 운영체제, 기타 프로세스들과의 상호작용에 필요한 메모리 공간
각 쓰레드별로 따로 할당된다.
메서드를 호출할 때마다 Frame 단위로 스택에 추가(push
) 된다.
pop
) 한다.각 Frame 에는 메서드의 지역 변수, 매개 변수, Operand Stack, Constant Pool Reference 로 이루어져 있다.
Java Stack 영역이 가득차면 StackOverflowError
발생
다른 프로그래밍 언어로 작성된 메서드를 다루는 영역
자바로만 처리할 수 없는 시스템 자원이나 API를 사용해야 하는 경우 다른 프로그래밍 언어로 작성된 Native Method 가 필요하다.
Java Stack 영역처럼 Native Method 가 실행될 경우, Stack 에 해당 메서드가 쌓인다.
명령어 주소값을 저장하는 공간
네이티브 메서드에서는 프로그램 카운터 값이 정의되지 않는다.
슬라이드 : https://speakerdeck.com/deepu105/jvm-memory-usage-stack-vs-heap
메모리에 적재된 클래스 (바이트코드) 들을 기계어로 변경하여 명령어 단위로 실행
Interpreter 방식과 JIT 컴파일러 방식이 있다.
런타임중에 바이트코드를 한 라인씩 읽으며 바로 실행한다.
C, C++ 처럼 미리 컴파일을 통해 기계어를 실행하는 언어에 비해 실행 속도가 느리다.
Just In Time 컴파일러 방식
자주 실행되는 바이트코드 영역을 런타임중에 기계어로 컴파일하여 사용한다.
컴파일 임계치를 만족하는 코드는 컴파일이 수행된다.
method entry counter (메소드 호출 횟수)
+ back-edge loop counter (메소드 내의 반복문 회전 횟수)
= 컴파일 임계치
JVM 상에서 더 이상 사용되지 않는 데이터가 할당되어있는 메모리를 해제시켜준다.
Heap 영역 내의 참조되지 않는 데이터가 주 대상이다.
Reachable : 참조되는 유효한 데이터
Unreachable : 참조되지 않는 데이터
- Heap 영역 내부의 객체들을 reachable 로 만들어주는 Method Area, Java Stack, Native Stack 을 root set 이라 한다.
- reachable 이 참조하는 다른 데이터도 reachable 이다.
GC 동작은 GC를 담당하는 쓰레드가 수행한다.
적절한 빈도의 GC 실행과, Stop-The-World 시간을 줄여 프로그램 성능을 높일 수 있다.
가비지 컬렉션때마다 모든 객체를 추적하는 것은 부하가 크다.
대부분의 객체는 빠르게 unreachable 해진다고 가정
오래된 영역에서 최신 영역으로의 참조 방향은 적다고 가정
두 가정을 바탕으로 대부분의 object 들은 생성된 지 얼마 안 되어 소멸된다는 것을 관찰하였다.
이를 최적화하기 위해 Heap 메모리를 세대 단위로 관리한다.
Mark And Sweep Algorithm
Mark Phase: root set에서 출발하여 참조되는 객체들을 마킹
Sweep Phase: 마킹되지 않은 객체들을 추적하며 삭제
메모리가 Fragmentation (파편화) 된다는 단점이 있다.
Mark And Compact Algorithm
Mark And Sweep Algorithm 을 진행 후, 메모리를 정리하여 메모리 Fragmentation 을 해결
주로 사용되는 알고리즘
Minor GC
주로 Young Generation 에서 일어난다.
Young Generation 안의 특정 영역이 가득 차 새로운 객체를 생성할 수 없을 때 발생
해당 영역은 객체가 빨리 모여 금방 가득차기 때문에 성능이 좋은 Minor GC를 사용한다.
마킹된 영역이 다음 영역으로 복사된다.
삭제 과정은 이루어지지 않는다.
Major GC에 비해 짧은 시간 Stop-The-World 가 발생해, 빠르다.
Major GC
주로 Old Generation 에서 일어난다.
앞서 다룬 GC 알고리즘이 적용된다.
긴 시간 Stop-The-World 가 발생해, 느리다.
출처
JVM에 관하여 - Part 1, JVM, JRE, JDK (테코블 - 와이비 님)
https://tecoble.techcourse.co.kr/post/2021-07-12-jvm-jre-jdk/
JRE (Red Hat)
https://www.redhat.com/en/topics/cloud-native-apps/what-is-a-Java-runtime-environment
JDK, JRE 그림 (Java Guides)
https://www.javaguides.net/2019/02/java-jvm-jre-jdk-explained-with-diagrams.html
JVM에 관하여 - Part 2, ClassLoader (테코블 - 와이비 님)
https://tecoble.techcourse.co.kr/post/2021-07-15-jvm-classloader/
Class Loader 이미지 (Java Tutorial Network)
https://javatutorial.net/tag/java-class-loader/
Class Loader Principle (Baeldung)
https://www.baeldung.com/java-classloaders
Class Loader in Java (GeeksforGeeks)
https://www.geeksforgeeks.org/classloader-in-java/
JVM에 관하여 - Part 3, Run-Time Data Area (테코블 - 와이비 님)
https://tecoble.techcourse.co.kr/post/2021-08-09-jvm-memory/
JVM Structure (Oracle)
https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-2.html
JVM Management (GeeksforGeeks)
https://www.geeksforgeeks.org/java-memory-management/
JVM에 관하여 - Part 3, Run-Time Data Area (테코블 - 와이비 님)
https://tecoble.techcourse.co.kr/post/2021-08-09-jvm-memory/
Heap & Stack 코드와 그림 (Deepu K Sasidharan 님)
https://dev.to/deepu105/visualizing-memory-management-in-jvm-java-kotlin-scala-groovy-clojure-19le
JVM Execution Engine (강준현 님)
https://junhyunny.github.io/information/java/jvm-execution-engine/
JVM 전체 그림 (DZone)
https://dzone.com/articles/jvm-architecture-explained
GC Weak Generational Hypothesis (브로리카 님)
https://brorica.tistory.com/entry/%EC%9E%90%EB%B0%94-gc
메모리 세대 그림 (Oracle)
https://docs.oracle.com/en/java/javase/16/gctuning/parallel-collector1.html#GUID-DCDD6E46-0406-41D1-AB49-FB96A50EB9CE
JVM에 관하여 - Part 4, Garbage Collection 기초 (테코블 - 와이비 님)
https://tecoble.techcourse.co.kr/post/2021-08-30-jvm-gc/