Java Virtual Machine (JVM) Core
Java Virtual Machine (JVM) Core의 정의, 범위, 선행 지식, 학습 주제, 참고 근거를 정리한 CS&E 학습 노드입니다.
Article
M
Me
hyunyoun's Blog
programming-languages-compilersprogramming-languagescompilersruntime-systemsmemory-managementjava-virtual-machine-jvm-corelanguages-compilerslearning9 min read
1. Overview
Java Virtual Machine 코어(Java Virtual Machine (JVM) Core)는 전 세계 금융 시스템, 빅데이터(Hadoop/Spark), 엔터프라이즈 백엔드(Spring)를 30년 가까이 지탱해 온 대표적인 런타임 시스템의 구조를 다룹니다.
학습자는 JVM의 메모리 공간 분할(Heap, Stack, Metaspace)과 동적 클래스 로딩 체계(Classloader Subsystem)를 살펴봅니다. 나아가 고성능 서버의 핵심 운영 지표인 **GC 튜닝 지표(Throughput vs Latency)**와 G1/ZGC의 내부 아키텍처, 런타임 성능 문제를 분석하는 스레드 덤프(Thread Dump)와 힙 덤프(Heap Dump) 분석 기법을 정리합니다. 마지막으로 JMM(Java Memory Model)의 가시성(Visibility) 보장과 volatile/synchronized의 저수준 메모리 배리어(Memory Barrier) 동작 방식까지 익혀 프로덕션 등급의 장애 대응 역량을 확보합니다.
2. Scope & Boundaries
In-Scope
- JVM 아키텍처 (JVM Architecture): 클래스로더(Bootstrap, Extension, App), 런타임 데이터 영역(Heap, JVM Stack, PC Register, Native Method Stack, Metaspace).
- GC 튜닝 및 최신 GC (GC Tuning & Modern GC): GC 지표(Latency/Pause time vs Throughput), G1 GC(Region 기반), ZGC(Colored Pointers, Load Barriers).
- 트러블슈팅과 모니터링 (Troubleshooting & Profiling): OOM(OutOfMemoryError) 원인 분석, 스레드 덤프(Deadlock, 락 경합 탐지), 힙 덤프 분석, JFR(Java Flight Recorder).
- 자바 메모리 모델 (Java Memory Model, JMM): Happens-Before 관계,
volatile메모리 가시성(Visibility), 스레드 세이프(Thread Safety).
Out-of-Scope
- Java 언어 문법 및 프레임워크: 클래스/인터페이스 설계, Spring 프레임워크 DI/AOP 원리 → Java 백엔드/프레임워크 영역.
- JIT 컴파일러 내부 C++ 구현: C1/C2 컴파일러 소스코드 분석 → 05-02-03 VM & JIT 심화 영역.
Boundaries
- Throughput GC vs Low-Latency GC 트레이드오프: Parallel GC(Throughput 중심)는 CPU 낭비 없이 애플리케이션 코드를 빠르게 실행하지만 GC 시 STW(일시 정지)가 깁니다(배치 처리에 적합). ZGC/Shenandoah(Low-Latency 중심)는 STW를 <1ms로 유지하지만 동시 마킹/이동을 위해 여분의 CPU 사이클을 소비하고 Load Barrier 오버헤드가 있습니다(실시간 API 응답에 적합). 상황에 맞는 GC 선택이 아키텍트의 역할입니다.
3. Counterexample
- 동기화 없는 상태 공유의 가시성 오류 (Visibility Bug in JMM):
boolean stop = false;루프 밖 변수. 스레드 A가while(!stop) {}로 무한 루프 실행. 스레드 B가stop = true;설정. 그러나 스레드 A는 멈추지 않고 영원히 돕니다. JMM(Java Memory Model)에서 CPU 캐시에 읽어온 변수를 메인 메모리와 즉시 동기화할 의무가 없으므로(가시성 부재), 스레드 A는 B의 변경을 평생 보지 못합니다.volatile boolean stop으로 선언하여 메모리 배리어(Memory Barrier)를 강제해야 해결됩니다. - 클래스로더 누수로 인한 Metaspace OOM (Classloader Leak): WAS(Tomcat 등)에서 애플리케이션을 핫 디플로이(Hot Deploy)할 때, 이전 애플리케이션의 클래스로더에 대한 참조(예: ThreadLocal 변수 누수)가 남아있으면, 해당 클래스로더가 로드한 수천 개의 클래스 메타데이터가 해제되지 못합니다. 디플로이를 반복할수록 Metaspace가 가득 차
java.lang.OutOfMemoryError: Metaspace로 서버가 크래시되는 고전적 엔터프라이즈 버그입니다.
4. Prerequisites
- 객체 지향 기본 (Basic): 클래스 인스턴스화, 메서드 호출 등의 자바/OOP 기본 지식. (05. PL)
- 가비지 컬렉션 기초 (Recommended): 세대별 GC, 마크-스윕 기본 개념. (05-02-04 Garbage Collection & Memory Management)
5. Learning Map
6. Learning Topics
Basic
Core Topic 01: 객체와 메타데이터의 거처, JVM 메모리와 클래스로더 (JVM Memory & Classloader)
- Why to Learn: "JVM 프로세스가 왜 2GB RAM을 달라고 했는데 OS 레벨에서는 3GB를 점유하는가?" 같은 프로덕션 메모리 관리 문제를 해결하고
OutOfMemoryError의 정확한 위치를 식별하기 위함입니다. - What to Learn:
- Concepts: Heap(객체 할당, GC 대상), Metaspace(Java 8 이후, 클래스 메타데이터, Native 메모리), JVM Stack(메서드 콜, 로컬 변수), PC Register, Native Method Stack.
- Skills: 클래스로더 위임 모델(Delegation Model: Bootstrap → Ext → App), JVM 플래그
-Xms,-Xmx,-XX:MaxMetaspaceSize.
- How to Learn:
- 1단계: 메모리 아키텍처:
new User()는 Heap에 할당, 그 참조 주소를 담는 변수user는 스레드 고유의 Stack 프레임에 할당,User.class의 구조(메서드 바이트코드)는 Metaspace에 할당되는 삼원 분리를 살펴봅니다. - 2단계: 클래스로더 위임:
java.lang.String을 로드할 때, 항상 Bootstrap 클래스로더에 먼저 위임하여 시스템 코어 클래스를 애플리케이션 코드가 위조(Spoofing)하는 것을 막는 보안/안전 흐름을 살펴봅니다.
- 1단계: 메모리 아키텍처:
- Implement: Java OOM 시뮬레이터 클래스 2개.
HeapOOM:List에 1MB 배열 무한 추가 →java.lang.OutOfMemoryError: Java heap space.StackOOM: 재귀 함수 무한 호출(deepCall()) →java.lang.StackOverflowError. 힙과 스택의 한계 공간 차이를 콘솔 로그로 증명.
Recommended
Core Topic 02: 예측 가능한 일시 정지, G1 GC와 ZGC 아키텍처 (GC Architecture & Tuning)
- Why to Learn: 트래픽이 폭주하는 서버에서 구식 CMS/Parallel GC의 긴 일시 정지(수 초)로 인한 타임아웃 장애를 해결하고, G1 GC 및 최신 ZGC의 구조를 이해하여 서버 튜닝 역량을 갖추기 위함입니다.
- What to Learn:
- Concepts: G1 GC(Garbage-First: 힙을 2000여 개 Region으로 분할, 혼합 수집), ZGC(Colored Pointers, Load Barriers, 크기 독립적 일시 정지 <1ms).
- Skills: GC 로그 분석
-Xlog:gc, 일시 정지 목표-XX:MaxGCPauseMillis.
- How to Learn:
- 1단계: G1 GC: 큰 힙을 고정된 Young/Old 영역 대신 Region 단위로 나눕니다. GC 시 "살아있는 객체가 가장 적어 쓰레기 수집 효율이 높은(Garbage-First)" Region을 우선적으로 스윕/압축(Compaction)하여 정해진 시간(예: 200ms) 내에 GC를 끝내는 흐름을 살펴봅니다.
- 2단계: ZGC Load Barrier: 객체 참조 포인터의 상위 비트(Color)를 사용해 상태(Remapped 등) 표시. 앱 스레드가 힙에서 참조를 읽어올 때(Load)마다 검사 코드(Load Barrier)가 가로채어, 객체가 GC에 의해 이동 중이면 올바른 주소로 수정 후 반환. 이로 인해 마킹과 메모리 복사(이동) 중에도 STW를 회피하는 ZGC 구조를 살펴봅니다.
- Implement: JVM 플래그 벤치마크 테스트 스크립트 작성 (Java). 대량 객체 할당/해제 루프 스크립트. 실행 1:
-XX:+UseParallelGC. 실행 2:-XX:+UseG1GC -XX:MaxGCPauseMillis=50. 실행 3:-XX:+UseZGC. GC 로그를 출력받아 파이썬으로 파싱, 각 GC 알고리즘의 99퍼센타일(P99) 최대 일시정지 시간 차이 수치 비교 차트 생성.
Practical
Core Topic 03: 프로덕션 서버의 장애 분석, 트러블슈팅과 덤프 분석 (JVM Troubleshooting & Profiling)
- Why to Learn: "서버 CPU가 100%를 치고 응답이 없습니다"라는 장애 상황에서 스레드 덤프를 떠서 병목 지점을 찾고, 힙 덤프로 메모리 누수의 원인이 되는 객체를 찾아내는 운영 대응 역량을 갖추기 위함입니다.
- What to Learn:
- Concepts: 스레드 덤프(Thread Dump: Runnable, Blocked, Waiting 상태), 힙 덤프(Heap Dump: hprof 포맷), 메모리 누수 트리(Dominator Tree), JFR(Java Flight Recorder).
- Skills:
jcmd,jstack,jmap, Eclipse MAT / VisualVM 사용법.
- How to Learn:
- 1단계: 스레드 덤프 분석:
jstack <pid>.BLOCKED (on object monitor)스레드가 수십 개 쌓여있고, 한 스레드가 DB 쿼리(SocketRead)를 기다리며 해당 모니터 락을 소유(locked)하고 있는 상태. 이 한 스레드 때문에 서버 전체가 정지한 병목 메커니즘을 분석합니다. - 2단계: 힙 덤프와 지배자 트리(Dominator Tree):
jmap -dump:live,format=b,file=heap.bin <pid>. MAT 툴에서 Retained Size(이 객체가 해제되면 함께 해제될 메모리 총량)가 높은 객체 탐색.Static Map객체가 1GB를 보유한 전역 캐시 메모리 누수 패턴을 살펴봅니다.
- 1단계: 스레드 덤프 분석:
- Implement: 데드락 발생 Java 프로그램
DeadlockSim제작. T1(락A→락B), T2(락B→락A) 실행 후 서버 정지 상황 유발. 별도 파이썬 스크립트에서jstack <pid>명령어를 서브프로세스로 실행, 출력 로그에서Found one Java-level deadlock:텍스트를 Grep 파싱하여 자동 데드락 알림 데모 시연.
Advanced
Core Topic 04: CPU 캐시와 재배치 제어, JMM과 메모리 가시성 (JMM & Memory Visibility)
- Why to Learn: 멀티스레드 코드에서 발생하는 재현하기 어려운 버그들은 OS/하드웨어의 명령어 재배치와 L1/L2 캐시 동기화 지연에서 비롯될 수 있습니다. JMM(Java Memory Model)을 이해하여 이런 환경에서 안전한 동기화 기준을 세우기 위해서입니다.
- What to Learn:
- Concepts: JMM(Java Memory Model), 명령어 재배치(Instruction Reordering), 가시성(Visibility), Happens-Before 원칙, 메모리 배리어(Memory Barrier).
- Skills:
volatile(가시성 보장 및 재배치 금지, 원자성 없음),synchronized(상호 배제 + 메모리 가시성 동시 보장).
- How to Learn:
- 1단계: 가시성과 캐시(Visibility): 스레드 A가 CPU 0, 스레드 B가 CPU 1에서 실행. 공유 변수
x. A가x=1을 자기 L1 캐시에만 쓰고 메인 메모리 반영(Flush) 지연. B는 여전히x=0으로 읽음.volatile int x선언 시, 즉시 캐시 무효화 및 메모리 Flush를 강제하는 메모리 배리어 동작을 살펴봅니다. - 2단계: Happens-Before 규칙: JMM이 보장하는 순서. "스레드 A가 락 해제 -> 스레드 B가 동일 락 획득" 시 A의 모든 메모리 쓰기가 B에게 보임(Happens-Before). 이 규칙에 의존하지 않는 스레드 간 데이터 공유는 데이터 경쟁(Data Race) 버그가 될 수 있음을 살펴봅니다.
- 1단계: 가시성과 캐시(Visibility): 스레드 A가 CPU 0, 스레드 B가 CPU 1에서 실행. 공유 변수
- Implement: Java 가시성 버그 재현 프로그램 작성.
volatile없는 공유boolean flag = true;. 루프 스레드가while(flag)대기, 메인 스레드 1초 후flag=false. 루프 스레드가 영원히 끝나지 않고 JIT 컴파일러가while(true)로 최적화하는(재배치) 현상 증명. 변수를volatile로 변경 후 즉시 종료되는 JMM 배리어 효과 비교 검증.
7. Terminology
8. References
Primary
- [P1] CS2023 - Programming Languages (PL) - Virtual Machines
- [P5] SFIA - Software Design (SWDN) - Environment Optimization
Secondary
- [Java Performance: The Definitive Guide] Scott Oaks - HotSpot JVM and GC Tuning
- [Understanding the JVM] Bill Venners - Classloader and Execution Engine
Industry
- [OpenJDK HotSpot Documentation] - Architecture of the HotSpot VM, Tiered Compilation
- [GraalVM Reference Manual] - Native Image and Ahead-of-Time (AOT) Compilation
9. Final Checklist
Primary
- WORA(Write Once, Run Anywhere) 철학이 바이트코드(
.class)와 OS별 JVM 구현체(Windows용, Linux용)의 역할 분담을 통해 어떻게 성립하는지 설명할 수 있는가? - JVM 메모리 구조에서 스레드마다 독립적으로 생성되는 Stack 영역과, 모든 스레드가 공유하는 Heap 영역의 차이를 증명할 수 있는가?
Secondary
- HotSpot VM이 인터프리터(빠른 시작)로 코드를 실행하다가 카운터가 임계치를 넘으면 C1, C2 컴파일러로 네이티브 코드로 변환하는 티어드 컴파일(Tiered Compilation) 메커니즘을 설명할 수 있는가?
- 클래스로더(ClassLoader)의 계층적 위임 모델(Delegation Model)이 코어 자바 라이브러리(rt.jar)의 악의적 변조를 어떻게 방어하는지 논증할 수 있는가?
Industry
- 다형성(Polymorphism)으로 인해 인터페이스 구현체가 런타임에 바뀌는 자바 특성상, JIT 컴파일러가 인라이닝(Inlining)을 시도하다가 디옵티마이제이션(Deoptimization)을 겪는 병목 시나리오를 설계할 수 있는가?
- 람다(Lambda/Serverless) 환경에서 JVM의 무거운 초기 구동(Cold Start) 문제를 해결하기 위해, GraalVM의 AOT(Ahead-Of-Time) 네이티브 이미지를 도입할 때 발생하는 리플렉션(Reflection) 제약 트레이드오프를 저울질할 수 있는가?