프로그래밍/Java

[Java] JVM, JRE, JDK, JIT 차이와 포함 관계

김수작 2025. 8. 14. 01:40

 

처음 Java를 공부하면서, JVM, JRE, JDK라는 용어를 접했지만,

이름이 비슷하고 서로 포함 관계를 가지다 보니 헷갈리기 쉬웠습니다.

여기에 JIT라는 개념까지 더해져 구분하기 더 힘들었습니다.

 

이 글에서는 JVM, JIT, JRE, JDK의 정의와 차이를 중심으로 정리해보고자 합니다.

각 요소가 정확히 무엇을 의미하고, 서로 어떤 관계로 얽혀 있는지 이해하는 데 초점을 맞추었습니다.

 


 

Java의 실행 철학

Java의 핵심 목표 중 하나는 아래와 같습니다.

"한 번 작성하면 어디에서나 실행(Write Once, Run Anywhere)"

 

이는 운영체제(OS)마다 코드를 따로 작성하거나 수정하는 번거로움 없이,

같은 프로그램을 여러 환경에서 동일하게 실행할 수 있도록 하기 위함입니다.

 

이를 위해 Java는 운영체제에 종속되지 않은 바이트코드와 JVM 구조를 채택하였습니다.

JVM만 해당 환경에 맞게 구현되어 있다면, 동일한 바이트코드가 어떤 운영체제에서도 실행될 수 있도록 말입니다.

 

즉, JVM을 사용하는 이유는 어떤 운영체제 위에서도 실행 가능하게 하기 위함입니다.

바이트코드는 모든 나라 사람들이 읽을 수 있는 '국제 공용어'와 같으며,

각 운영체제의 JVM은 이를 해당 환경의 기계어로 번역하는 '통역사'의 역할을 한다고 볼 수 있습니다.


Java 실행 · 개발 환경

Java로 개발하거나 실행을 하다 보면 JVM, JIT, JRE, JDK 같은 용어를 자주 접하게 됩니다.

하지만, 처음 배우는 입장에서는 이들이 서로 어떤 관계에 있는지, 또 어떤 역할을 하는지 헷갈리기 쉽습니다.

 

Java의 실행·개발 환경 각 구성 요소의 의미와 역할, 그리고 이들이 어떻게 맞물려

Java 프로그램이 개발되고 실행되는지를 차례대로 살펴보겠습니다.

 

 

1) JVM (Java Virtual Machine)

자바 바이트코드(.class 파일)를 해석하고 실행하는 가상 머신

 

JVM'자바를 실행하기 위한 가상 머신'입니다.

쉽게 말해, 운영체제 위에 설치되어 자바 프로그램이 동작하도록 돕는 가상의 컴퓨터라고 할 수 있습니다.

 

일반적인 C/C++ 프로그램은 소스코드(.c, .cpp)를 컴파일하면 기계어(바이너리 코드)가 생성되어 CPU가 바로 실행됩니다.

하지만 Java의 경우, 소스코드(.java)를 자바 컴파일러(javac)가 컴파일하면 기계어(바이너리 코드)가 아닌 바이트코드(.class)로 변환합니다.

 

바이트코드는 특정 OS나 CPU에 종속되지 않은 중간 언어이므로 CPU가 직접 이해하지 못합니다.

따라서 JVM이 바이트코드를 읽어 해석(인터프리트)하거나, JIT 컴파일러를 사용해 기계어로 변환하여 실행합니다.

 

바이트코드는 JVM 위에서 실행되기 때문에,

같은 .class 파일이라도 윈도우, 맥, 리눅스 등 어떤 환경에서든 JVM만 설치되어 있다면 실행할 수 있습니다.

💾 바이트코드 vs 바이너리 코드
- 바이트코드 : JVM이 이해하는 OS·CPU 독립적인 중간 언어
- 바이너리 코드 : CPU가 직접 실행하는 OS·CPU 종속적인 기계어

 

 

 

2) JIT 컴파일러

JVM 실행 엔진 내부에서 동작하는 동적 컴파일러

 

기본적으로 JVM은 인터프리터를 사용해 바이트코드를 한 줄씩 해석하고 실행합니다.

하지만 이 방식은 반복 실행되는 코드에서 속도가 느려질 수 있기 때문에, 성능 향상을 위해 JIT 컴파일러가 함께 사용됩니다.

 

JIT 컴파일러는 실행 중에 자주 실행되는 코드(핫스팟)를 찾아 해당 부분만 기계어로 변환하고,

변환된 기계어를 캐시에 저장합니다.

이후 동일한 코드가 실행될 때는 인터프리터를 거치지 않고 기계어를 직접 실행하여 속도를 높입니다.

 

처음부터 모든 코드를 JIT 컴파일하면 좋을 것 같지만,

프로그램 시작 시 전체를 변환하는 데 시간이 오래 걸립니다.

따라서 Java는 인터프리터로 빠르게 실행을 시작하고, 실행 도중 필요한 부분만 JIT 컴파일하여 선택적으로 최적화하기 때문에 

초기 실행 속도장기 실행 성능을 모두 확보합니다.

🗣️ 인터프리터 vs 컴파일러
- 인터프리터 : 코드를 실행 시점에 한 줄씩 해석하여 바로 실행하는 방식
- 컴파일러 : 실행 전에 전체 코드를 기계어로 변환한 뒤 실행하는 방식

 

 

 

3) JRE (Java Runtime Environment)

자바 프로그램을 실행하기 위한 실행 환경

 

자바 프로그램이 동작하려JVM뿐만 아니라,

프로그램이 필요로 하는 각종 자바 클래스 라이브러리 실행 설정도 함께 갖추어야 합니다.

 

JRE는 내부에 JVM을 포함하며, java.lang, java.util과 같은 자바 표준 라이브러리 클래스,

그리고 실행에 필요한 설정 파일로 구성됩니다.

즉, JRE는 자바 프로그램이 문제없이 실행되도록 실행 도구와 자원을 모두 제공하는 패키지입니다.

 

개발자는 JDK를 통해 프로그램을 만들고, 완성된 프로그램은 JRE 환경에서 실행됩니다.

따라서 자바를 단순히 "실행만" 하려는 경우에는 JRE만 설치해도 충분합니다.

 

 

 

4) JDK (Java Development Kit)

자바 개발을 위한 모든 기능을 갖춘 개발 키트

 

자바 프로그램을 작성하고, 컴파일하고, 실행할 수 있도록 JRE와 JVM, 그리고 각종 개발 도구를 포함합니다.

 

JDK 안에는 javac(컴파일러), javadoc(문서 생성기), jar(압축/패키징 도구) 등과 같은 개발용 명령어 도구들이 함께 들어있습니다.

즉, JDK를 사용하면 프로그램 개발과 실행을 모두 할 수 있으며, JRE를 따로 설치할 필요가 없습니다.

 

개발자는 JDK로 프로그램을 작성·컴파일하고, 완성된 프로그램은 JRE 환경에서 실행됩니다.

즉, 단순히 자바 프로그램 실행만 필요하다면 JRE로 충분하지만, 개발하려면 반드시 JDK가 필요합니다.

 

또한 JDK 안에 JRE가 이미 포함되어 있어 개발자는 JDK 하나만 설치하면 개발과 실행 모두 가능합니다.

예전에는 JRE를 별도로 다운로드할 수 있었지만, 최근에는 JDK 배포에 통합되어 공식 JRE 단독 배포가 거의 없습니다.

현재는 개발자뿐 아니라 실행만 하는 경우에도 JDK를 설치하는 것이 일반적이며, JRE만 따로 설치하는 경우는 드뭅니다.


한눈에 보기

구분 역할/목적 관계 구성 요소 대상
JDK 개발 + 실행 최상위 JRE + Java 개발 도구 개발자
JRE 실행 JDK에 포함 JVM + Java 클래스 라이브러리 실행 환경
JVM 바이트코드 실행 JRE에 포함 클래스 로더, 실행 엔진(인터프리터 + JIT), GC(가비지 컬렉터) 런타임
JIT 성능 최적화 JVM 내부 런타임 동적 컴파일러 JVM 성능 향상

 

위 이미지와 표는 JDK, JRE, JVM, JIT의 포함 관계와 각 구성 요소의 역할을 한눈에 보여줍니다.

JDK는 자바 프로그램을 개발하고 실행하기 위한 최상위 패키지로, JRE와 개발 도구를 포함합니다.

JRE자바 프로그램을 실행하기 위한 환경으로, 내부에 JVM과 자바 클래스 라이브러리를 가지고 있습니다.

JVM바이트코드를 실행하는 핵심 엔진이며, 클래스 로더, 실행 엔진(인터프리터와 JIT), 가비지 컬렉터 등으로 구성됩니다.

JIT는 JVM 내부에서 동작하는 성능 최적화 도구로, 실행 중 자주 사용되는 코드를 기계어로 변환하여 속도를 향상시킵니다.

 

이처럼 각 요소는 계층적으로 포함 관계를 이루고 있으며, JDK → JRE → JVM → JIT 순으로 범위가 좁아집니다.

개발자는 JDK를 사용하여 프로그램을 작성·컴파일·실행하고, 최종 사용자는 JRE 환경에서 프로그램을 실행하게 됩니다.

 


정리하며

처음에는 JVM, JRE, JDK, JIT의 개념이 서로 헷갈렸지만, 포함 관계와 역할을 차근차근 정리하니 훨씬 명확해졌습니다.
특히 개발을 하면서 매번 JDK 파일을 다운로드했으면서도,

JDK가 정확히 무엇인지 깊이 생각해 본 적이 없었는데 이번에 제대로 알게 되었습니다.

 

또한 JVM도 얼마 전 컴활 필기 공부를 하면서 접했던 개념이었지만, 당시에는 OS에 관계없이 사용할 수 있다는 것이 정확히 어떤 의미인지, 그리고 바이트코드가 무엇인지 의문만 품고 넘어갔습니다.

이번에 이 내용을 정리하면서 그 의문까지 해결되어서 정말 좋았습니다.

 

이제 각 구성 요소가 어디에 속하고 어떤 역할을 하는지 그림이 그려지고,

Java 실행·개발 환경에 대한 기초를 확실히 다진만큼 앞으로 더 깊이 이해하고 활용할 수 있을 것 같습니다.