Java를 처음 설치할 때 “JDK를 받으세요”라는 말을 들었지만, JVM이나 JRE와 무슨 관계인지 모른 채 그냥 설치한 경험이 있으신가요? 면접에서 “JVM이 뭔지 설명해 보세요”라는 질문을 받으면 막막한 경우가 많습니다. JVM JRE JDK 차이를 명확하게 이해하는 것은 단순히 용어를 외우는 것이 아니라, Java가 “한 번 작성하면 어디서든 실행된다(Write Once, Run Anywhere)”는 철학이 어떻게 구현되는지를 이해하는 일입니다. 이 글에서는 세 개념의 정의와 포함 관계, Java 코드가 실행되기까지의 전체 흐름, JVM 내부 구조, Java 9 이후의 변화, 그리고 면접 답변 공식까지 완전히 정리합니다.
목차
- 세 개념의 포함 관계부터 — 마트료시카 구조 이해
- JVM(Java Virtual Machine) — 플랫폼 독립성의 핵심
- JRE(Java Runtime Environment) — 실행에 필요한 모든 것
- JDK(Java Development Kit) — 개발에 필요한 모든 것
- Java 코드가 실행되기까지 — 전체 흐름 완벽 정리
- Java 9 이후 변화와 면접 답변 공식
1. 세 개념의 포함 관계부터 — 마트료시카 구조 이해
한 줄 요약부터
세 개념의 관계를 가장 간결하게 표현하면 이렇습니다.
JDK ⊃ JRE ⊃ JVM
JVM : 바이트코드를 실제로 실행하는 가상 머신 (엔진)
JRE : JVM + 표준 라이브러리 (실행 환경)
JDK : JRE + 개발 도구 (개발 + 실행 환경)
JDK = JRE + α이며, JRE는 읽기 전용, JDK는 읽기·쓰기 전용이라고 생각할 수 있습니다. Molit
마트료시카(러시아 인형) 비유
세 개념은 마치 러시아 전통 인형(마트료시카)처럼 서로 포함하는 관계입니다. 가장 안쪽 작은 인형이 JVM, 그 바깥이 JRE, 가장 바깥이 JDK입니다.
┌─────────────────────────────────────────────┐
│ JDK (Java Development Kit) │
│ ┌───────────────────────────────────────┐ │
│ │ JRE (Java Runtime Environment) │ │
│ │ ┌─────────────────────────────────┐ │ │
│ │ │ JVM (Java Virtual Machine) │ │ │
│ │ │ - 클래스 로더 │ │ │
│ │ │ - JIT 컴파일러 │ │ │
│ │ │ - 가비지 컬렉터 │ │ │
│ │ └─────────────────────────────────┘ │ │
│ │ + 표준 라이브러리 (java.lang, java.util)│ │
│ └───────────────────────────────────────┘ │
│ + javac (컴파일러) │
│ + javadoc, jar, jdb (개발 도구들) │
└─────────────────────────────────────────────┘
이 포함 관계를 이해하면 “나는 JDK를 설치했는데 JVM이 없는 건가요?”라는 질문에 바로 답할 수 있습니다. JDK를 설치하면 JRE도 JVM도 모두 포함되어 있습니다.
2. JVM(Java Virtual Machine) — 플랫폼 독립성의 핵심
JVM의 정의
JVM은 자바 가상 머신으로, 자바 프로그램을 실행하기 위한 가상 컴퓨터 역할을 합니다. 가장 중요한 점은 JVM이 자바와 운영체제 사이에서 중계 역할을 하여 자바가 플랫폼 독립적으로 실행 가능하게 해준다는 것입니다. Daishin
JVM은 자바 소스 코드를 직접 읽지 않습니다. javac 컴파일러가 소스 코드(.java)를 **바이트코드(.class 파일)**로 변환하고, JVM이 이 바이트코드를 읽어 각 운영체제가 이해할 수 있는 기계어로 변환해 실행합니다.
Hello.java (소스 코드)
↓ javac 컴파일
Hello.class (바이트코드)
↓ JVM 실행
운영체제별 기계어 (Windows / Linux / macOS 각각)
Java가 플랫폼 독립적인 이유
C, C++, Rust, Go 같은 언어는 OS마다 다시 컴파일해야 합니다. 소스 코드가 바로 특정 OS의 기계어로 변환되기 때문입니다. 하지만 자바는 JVM을 이용해서 동일한 자바 바이트코드를 각 운영체제 환경에서도 실행할 수 있습니다. JVM은 운영체제별로 구현이 되어 있습니다. 공통 중간 산출물인 바이트코드를 OS별 JVM이 실행하는 구조입니다. 이것이 JVM의 특징인 플랫폼 독립성입니다. Molit
Hello.class (바이트코드)
│
┌────────────┼────────────┐
↓ ↓ ↓
JVM for Windows JVM for Linux JVM for macOS
↓ ↓ ↓
Windows 기계어 Linux 기계어 macOS 기계어
같은 .class 파일 하나로 Windows, Linux, macOS 모두에서 실행됩니다. 개발자는 “Write Once, Run Anywhere”의 혜택을 누립니다.
JVM 내부 구조 — 3개 핵심 컴포넌트
JVM은 크게 세 가지 핵심 컴포넌트로 구성됩니다.
① 클래스 로더 (Class Loader)
.class 파일을 JVM 메모리에 로드합니다. 필요한 클래스를 처음 사용할 때 동적으로 로딩하는 방식으로 작동합니다. Bootstrap → Extension → Application 세 단계로 로딩을 시도합니다.
② 실행 엔진 (Execution Engine) — 인터프리터 + JIT 컴파일러
로드된 바이트코드를 실제로 실행합니다. 두 가지 방식으로 동작합니다.
- 인터프리터(Interpreter): 바이트코드를 한 줄씩 해석해 실행합니다. 시작이 빠르지만 반복 실행 시 느립니다.
- JIT 컴파일러(Just-In-Time Compiler): 자주 반복되는 코드를 감지해 통째로 기계어로 컴파일해 캐시합니다. 이후 같은 코드를 만나면 캐시된 기계어를 바로 실행해 속도가 크게 향상됩니다.
바이트코드 → 인터프리터 (처음 실행 — 느림)
→ JIT 컴파일 (반복 코드 감지 — 최적화)
→ 캐시된 기계어 (이후 재실행 — 매우 빠름)
③ 가비지 컬렉터 (Garbage Collector, GC)
더 이상 참조되지 않는 객체를 자동으로 찾아 메모리에서 해제합니다. C/C++에서 개발자가 직접 malloc/free로 메모리를 관리해야 하는 것과 달리, Java에서는 GC가 자동으로 처리합니다. 개발자는 메모리 누수 걱정 없이 비즈니스 로직에 집중할 수 있습니다.
JVM 메모리 구조 (Runtime Data Area)
JVM은 실행 중 데이터를 여러 메모리 영역으로 나눠 관리합니다.
| 메모리 영역 | 저장 내용 | 공유 여부 |
|---|---|---|
| 메서드 영역 (Method Area) | 클래스 정보, 정적 변수, 상수 | 모든 스레드 공유 |
| 힙 (Heap) | new로 생성된 객체·배열 | 모든 스레드 공유 |
| 스택 (Stack) | 메서드 호출 프레임, 지역 변수 | 스레드별 독립 |
| PC 레지스터 | 현재 실행 중인 명령어 주소 | 스레드별 독립 |
| 네이티브 메서드 스택 | 네이티브(C/C++) 메서드 실행 | 스레드별 독립 |
3. JRE(Java Runtime Environment) — 실행에 필요한 모든 것
JRE의 정의
JRE는 JVM을 포함하여 자바 프로그램을 실행하기 위해 필요한 자바 클래스 라이브러리, 그 밖에 다른 지원 파일을 포함하는 자바 실행 환경입니다. 컴파일러가 없기 때문에 오직 실행만 가능합니다. Daishin
JRE의 구성을 나누면 이렇습니다.
JRE
├── JVM (Java Virtual Machine)
│ ├── 클래스 로더
│ ├── 실행 엔진 (인터프리터 + JIT)
│ └── 가비지 컬렉터
└── 표준 클래스 라이브러리
├── java.lang (String, Object, System 등 핵심)
├── java.util (ArrayList, HashMap 등 자료구조)
├── java.io (파일·스트림 입출력)
├── java.net (네트워크 통신)
└── java.sql (데이터베이스 연결)
표준 라이브러리는 우리가 import java.util.ArrayList;라고 쓸 때 가져다 쓰는 바로 그것입니다. JVM만으로는 이 라이브러리들이 없어 실제 앱을 실행하기 어렵습니다. JRE가 JVM과 이 라이브러리를 함께 패키징해 실제 앱 실행이 가능한 환경을 만드는 것입니다.
JRE만 있으면 되는 경우
JAVA 언어를 사용하는 개발자가 아니라 JAVA 언어로 만들어진 프로그램을 실행하는 사용자라면 JRE만 컴퓨터에 설치하면 됩니다. Esrks-reit
예를 들어 Java로 만들어진 게임이나 데스크탑 앱을 사용하는 일반 사용자는 소스 코드를 컴파일할 필요가 없습니다. 이미 컴파일된 .class 파일(또는 .jar 패키지)을 실행하기만 하면 되므로 JRE만으로 충분합니다.
4. JDK(Java Development Kit) — 개발에 필요한 모든 것
JDK의 정의
JDK는 Java Development Kit의 약자로, 자바 프로그램을 개발·컴파일·실행·디버깅할 수 있도록 JVM과 표준 라이브러리, 그리고 개발 도구들을 포함한 종합 개발 환경입니다. Molit
자바 언어 개발자는 자바 언어로 작성된 소스를 컴파일하고 관리합니다. 이때 사용되는 도구를 JDK라고 합니다. Esrks-reit
JDK
├── JRE (실행 환경 전체)
│ ├── JVM
│ └── 표준 라이브러리
└── 개발 도구 (Development Tools)
├── javac : Java 컴파일러 (.java → .class)
├── java : Java 실행기 (JVM 실행)
├── javadoc : API 문서 자동 생성기
├── jar : .jar 파일 패키징 도구
├── jdb : Java 디버거
├── jconsole : JVM 모니터링 도구
└── jstack : 스레드 덤프 출력 도구
JDK 핵심 도구 — javac와 java의 차이
bash
# 1단계: .java 소스 파일을 .class 바이트코드로 컴파일
$ javac Hello.java
# → Hello.class 파일 생성
# 2단계: JVM으로 .class 파일 실행
$ java Hello
# → Hello.class를 JVM이 로드하고 실행
# → "Hello, World!" 출력
javac는 JDK에만 있습니다. JRE에는 javac가 없습니다. 소스 코드를 컴파일해야 한다면 반드시 JDK가 필요합니다.
OpenJDK vs Oracle JDK
JDK를 설치하려 할 때 OpenJDK와 Oracle JDK 중 어느 것을 선택할지 고민이 됩니다.
| 구분 | OpenJDK | Oracle JDK |
|---|---|---|
| 라이선스 | GPL (무료·오픈소스) | Oracle NFTC (상업용 유료) |
| 성능 차이 | 사실상 동일 | 일부 독자 최적화 포함 |
| 지원 기간 | 6개월마다 새 버전 | LTS 버전 장기 지원 |
| 권장 사용처 | 개발·학습·비상업적 프로젝트 | 엔터프라이즈 상업 환경 |
Java 9 버전 이상부터는 ‘JRE’라는 독립 패키지를 따로 배포하지 않는 경우가 많고, 대신 JDK 자체가 런타임을 포함합니다. 따라서 현재 개발자 환경에서는 JDK를 설치하면 모든 것이 해결됩니다. Molit
5. Java 코드가 실행되기까지 — 전체 흐름 완벽 정리
지금까지 배운 내용을 하나의 흐름으로 연결해 봅니다.
java
// 1. 개발자가 소스 코드 작성
// Hello.java
public class Hello {
public static void main(String[] args) {
System.out.println("Hello, JVM!");
}
}
전체 실행 흐름:
[작성] Hello.java
│
│ javac (JDK의 자바 컴파일러)
↓
[컴파일] Hello.class (바이트코드)
│
│ java 명령어 실행 → JVM 시작
↓
[로드] 클래스 로더 → Hello.class를 JVM 메모리에 로드
│
│ 실행 엔진
↓
[실행] 인터프리터로 바이트코드를 한 줄씩 실행
│ → 반복 구간 감지 시 JIT 컴파일러로 기계어 변환·캐시
↓
[출력] Hello, JVM!
│
│ 실행 중 GC (가비지 컬렉터)가 백그라운드에서 메모리 관리
이 흐름에서 각 역할을 정리하면 이렇습니다.
| 단계 | 역할 | 담당 컴포넌트 |
|---|---|---|
| 소스 작성 | .java 파일 작성 | 개발자 + IDE |
| 컴파일 | .java → .class | javac (JDK 포함) |
| 클래스 로딩 | .class → JVM 메모리 | 클래스 로더 (JVM) |
| 실행 | 바이트코드 → 기계어 실행 | 인터프리터 + JIT (JVM) |
| 메모리 관리 | 불필요 객체 자동 해제 | 가비지 컬렉터 (JVM) |
| 라이브러리 제공 | java.util 등 표준 API | 표준 라이브러리 (JRE) |
6. Java 9 이후 변화와 면접 답변 공식
Java 9 모듈 시스템 — JRE 독립 배포 종료
Java 8까지는 JRE와 JDK를 별도로 배포했습니다. 하지만 Java 9에서 모듈 시스템(Project Jigsaw)이 도입되면서 구조가 바뀌었습니다.
Java 9 버전 이상부터는 JRE라는 독립 패키지를 따로 배포하지 않는 경우가 많고, 대신 JDK 자체가 런타임을 포함합니다. Molit
더 중요한 변화는 jlink 도구의 등장입니다. 이제 개발자는 앱에서 실제로 사용하는 모듈만 골라 최소한의 커스텀 런타임을 만들 수 있습니다. 500MB짜리 전체 JRE 대신 필요한 것만 포함한 50MB 런타임을 앱과 함께 배포하는 것이 가능해졌습니다. Docker 컨테이너와 마이크로서비스 배포에서 특히 유리합니다.
bash
# jlink로 커스텀 최소 런타임 생성 (Java 9+)
jlink \
--module-path $JAVA_HOME/jmods \
--add-modules java.base,java.net.http \
--output custom-runtime
LTS 버전 선택 기준
현업에서 JDK 버전을 선택할 때는 LTS(Long-Term Support) 버전을 기준으로 합니다.
| 버전 | LTS 여부 | 지원 종료 | 주요 특징 |
|---|---|---|---|
| Java 8 | ✅ LTS | 2030년 | 레거시 시스템 대부분, 람다 도입 |
| Java 11 | ✅ LTS | 2026년 | 모듈 시스템 안정화, var 키워드 |
| Java 17 | ✅ LTS | 2029년 | 레코드·봉인 클래스·패턴 매칭 |
| Java 21 | ✅ LTS | 2031년 | 가상 스레드(Project Loom) |
새 프로젝트를 시작한다면 Java 21 LTS를 선택하는 것이 현재 기준으로 가장 권장됩니다.
면접 답변 공식
Q. JVM, JRE, JDK의 차이를 설명해 주세요.
"세 개념은 서로 포함 관계입니다.
JVM은 자바 가상 머신으로, 바이트코드(.class 파일)를
각 운영체제의 기계어로 변환해 실행하는 핵심 엔진입니다.
클래스 로더·JIT 컴파일러·가비지 컬렉터로 구성되며,
Java의 플랫폼 독립성(Write Once, Run Anywhere)을
가능하게 하는 핵심 기술입니다.
JRE는 Java Runtime Environment로, JVM과
표준 클래스 라이브러리(java.lang, java.util 등)를
합친 실행 환경입니다. 이미 빌드된 자바 앱을
실행하는 데 필요한 모든 것을 포함하며,
소스 코드를 컴파일하는 기능은 없습니다.
JDK는 Java Development Kit로, JRE 전체와
javac(컴파일러), javadoc, jar 등 개발 도구를
추가한 종합 개발 환경입니다.
자바 프로그램을 작성하고 컴파일하려면 JDK가 필요합니다.
정리하면 JDK ⊃ JRE ⊃ JVM이며,
Java 9 이후로는 JRE를 별도 배포하지 않아
개발자는 JDK 하나만 설치하면 됩니다."
자주 나오는 심화 질문 Q&A
Q1. JVM은 플랫폼 독립적이지만 JVM 자체는 플랫폼 종속적이다 — 무슨 의미인가?
자바 바이트코드는 플랫폼 독립적입니다. 어떤 OS에서 컴파일한 .class든 모든 OS에서 실행됩니다. 그러나 JVM 소프트웨어 자체는 OS별로 다르게 구현됩니다. Windows용 JVM, Linux용 JVM, macOS용 JVM이 각각 따로 존재합니다. 즉, 자바 프로그램(바이트코드)이 독립적이기 위해 JVM이 플랫폼마다 존재하는 것입니다.
Q2. JIT 컴파일러가 없으면 자바가 느린 이유는?
JIT가 없다면 JVM은 바이트코드를 한 줄씩 해석하는 순수 인터프리터 방식으로만 실행됩니다. 반복문이나 자주 호출되는 메서드는 매번 새로 해석해야 해 성능이 떨어집니다. JIT는 자주 실행되는 코드를 감지해 기계어로 컴파일·캐시하기 때문에, 실행이 지속될수록 성능이 올라가는 “워밍업(Warm-up)” 특성이 생깁니다.
Q3. javac와 JVM의 역할 분리가 왜 중요한가?
컴파일(javac)과 실행(JVM)을 분리한 덕분에 유연성이 생깁니다. Kotlin, Scala, Groovy 등 다른 언어로 작성한 코드도 컴파일하면 Java 바이트코드가 나오고, 같은 JVM에서 실행할 수 있습니다. JVM 생태계가 Java 언어에만 종속되지 않는 이유입니다.
결론
JVM JRE JDK 차이를 핵심 3문장으로 압축하면 이렇습니다. JVM은 바이트코드를 기계어로 실행하는 엔진이자 플랫폼 독립성의 핵심입니다. JRE는 JVM과 표준 라이브러리를 합친 실행 전용 환경으로, 컴파일 기능이 없습니다. JDK는 JRE에 개발 도구를 더한 것으로, Java 개발자라면 반드시 JDK를 설치해야 합니다. Java 9 이후로는 JRE 독립 배포가 사라지고 JDK 하나로 통합되었습니다. 이 세 개념의 포함 관계(JDK ⊃ JRE ⊃ JVM)와 각각의 역할을 이해하면 Java의 동작 원리 전체가 훨씬 명확해집니다.
ℹ️ 참고 안내 본 글의 내용은 Java 21 LTS 기준으로 작성되었습니다. JVM 내부 구조(GC 알고리즘, 클래스 로더 위임 모델 등)는 JDK 버전과 구현체(OpenJDK, GraalVM 등)에 따라 세부 사항이 달라질 수 있습니다. 더 깊은 내용은 Oracle 공식 Java 문서(docs.oracle.com/en/java)와 OpenJDK 프로젝트(openjdk.org)를 참고하시기 바랍니다.
답글 남기기