JVM 내부를 코드로 직접 구현하며 이해하는 교육용 프로젝트. 책이나 블로그 대신, 동작하는 코드와 719개의 테스트로 배웁니다.
javac HelloWorld.java → HelloWorld.class → 이 JVM으로 실행 ✅
byte[] { 0x10, 3, 0x10, 5, 0x60, 0xAC } → 이 JVM으로 실행 ✅
JVM을 공부할 때 "GC는 Mark-and-Sweep으로 동작한다"는 설명은 쉽게 찾을 수 있습니다.
하지만 Mark 단계에서 GC Roots를 어떻게 순회하는지, 클래스 로딩 시 <clinit>이 정확히 언제 실행되는지를
코드로 직접 확인할 수 있는 자료는 많지 않습니다.
이 프로젝트는 그 간극을 메웁니다.
6개 챕터로 구성되어 있으며, 각 주제는 개념 → 📄 구현 코드 → 🧪 테스트 3단계로 연결됩니다.
| 챕터 | 핵심 코드 | 테스트 |
|---|---|---|
| 1. 클래스 파일 파싱 | ClassFileParser, ConstantPool |
~20개 |
| 2. 스택 기반 실행 모델 | Interpreter, StackFrame |
~34개 |
| 3. 바이트코드 명령어 | InstructionSet, instruction/ |
~243개 |
| 4. 예외 처리 | ExceptionHandler, Interpreter.findHandlerPc() |
~47개 |
| 5. 클래스 로딩과 초기화 | ClassLoader, JVM.runClinitIfNeeded() |
~78개 |
| 6. 메모리 관리와 GC | MarkAndSweepGC, GenerationalHeap |
~124개 |
각 클래스의 Javadoc에는 JVM 스펙(§ 표기) 참조가 달려 있어, 코드와 스펙을 나란히 읽을 수 있습니다.
📖 CURRICULUM.md를 따라가는 것을 권장합니다.
커리큘럼은 챕터 1부터 순서대로 읽도록 설계되어 있으며, 각 섹션마다:
- 개념 설명과 JVM 스펙 참조
- 구현 코드 라인 직접 링크 (
ClassFileParser.java:66-79형식) - 실행 가능한 테스트 파일 링크
- 자기 점검 질문
이 갖춰져 있습니다. 처음이라면 챕터 1(클래스 파일 파싱)부터 시작하세요.
src/main/java/com/jaeyeonling/jvm/
├── classfile/ 클래스 파일 파싱 (ClassFileParser, ConstantPool, ClassLoader)
├── execution/ 실행 엔진 (Interpreter, ExecutionEngine, InstructionSet)
│ └── instruction/ 바이트코드 명령어 202개 (카테고리별 서브패키지)
├── runtime/ 런타임 데이터 구조 (RuntimeClass, RuntimeMethod, ExceptionHandler)
│ └── thread/ JVM 스레드 / 스택 프레임 (JVMThread, StackFrame, OperandStack)
├── memory/ 힙 / GC (Heap, GenerationalHeap, MarkAndSweepGC, ObjectHeader)
├── jdk/ JDK 네이티브 메서드 스텁 (String, Math, Integer 등)
└── cli/ 커맨드라인 인터페이스 (run, bytecode, info 명령어)
git clone https://github.com/jaeyeonling/java-jvm.git
cd java-jvm
# HelloWorld 실행 → "Hello, JVM!" 출력
./gradlew run --args="run -cp src/test/resources/samples HelloWorld"
# .class 파일 역어셈블 — 이 프로젝트의 ClassFileParser가 직접 파싱
./gradlew run --args="bytecode src/test/resources/samples/HelloWorld.class"역어셈블 출력 예시:
Classfile: HelloWorld.class
class HelloWorld
superclass: java.lang.Object
public static main([Ljava/lang/String;)V
Code:
0: getstatic #7 // Field java.lang.System.out:Ljava/io/PrintStream;
3: ldc #15 // String "Hello, JVM!"
5: invokevirtual #17 // Method java.io.PrintStream.println:(Ljava/lang/String;)V
8: return
직접 만든 Java 파일도 실행할 수 있습니다:
javac MyClass.java
./gradlew run --args="run -cp . MyClass"
./gradlew run --args="bytecode MyClass.class"./gradlew build
./gradlew test # 719개 테스트 전부 실행요구사항: Java 21+, Gradle 8+
교육 목적이므로 일부 기능은 단순화하거나 생략했습니다.
| 항목 | 상태 | 비고 |
|---|---|---|
.class 파일 파싱 |
✅ | Java 1~21 (major 45~65) |
| 바이트코드 실행 | ✅ | 202개 opcode |
예외 처리 (try/catch/finally) |
✅ | exception_table 기반 |
클래스 로딩 / <clinit> |
✅ | 부모 위임 모델 포함 |
| Mark-and-Sweep / Reference Counting GC | ✅ | 교육용 단순화 |
invokedynamic / 람다 |
기본 람다만 지원, 복잡한 BSM은 실패 | |
| 멀티스레드 실행 | ❌ | 단일 스레드만 지원 |
| JIT 컴파일 | ❌ | 인터프리터 전용 (이론은 CURRICULUM Part II-B) |