Java 프로그램이 실행되는 흐름
자바 프로그램은 단순히 .java 파일을 실행하는 것이 아니라, 컴파일 → 로드 → 실행 이라는 여러 단계를 거쳐 최종적으로 CPU 가 이해할 수 있는 기계어로 변환됩니다.
이 과정에서 JDK, JVM, JRE가 각각 어떤 역할을 하는지 이해하는 것이 중요합니다.
1. 소스 코드 작성과 컴파일
개발자는 자바 소스 파일(.java)을 작성
JDK 에 포함된 javac (Java Compiler)가 소스 코드를 컴파일 하여 JVM이 이해할 수 있는 바이트코드(.class 파일)로 변환
이 바이트 코드는 특정 운영체제에 종속되지 않으며, “Write Once, Run Anywhere”를 가능하게 하는 핵심
즉 .java → .class 변환 과정이 여기서 일어남
2. 클래스 로딩(Class Loading)
프로그램 실행 시 , JVM 의 클래스 로더(Class Loader)가 .class 파일을 읽어 JVM 메모리(Runtime Data Areas)에 올립니다.
클래스 로딩은 정적인 방식이 아닌 동적 로딩을 사용합니다.
동적 로딩이란?
프로그램 시작 시 모든 클래스를 한꺼번에 메모리에 적재하지 않고, 실제로 필요한 순간 (객체 생성, static 변수/ 메서드 참조 시)에만 메모리에 로딩하는 방식.
불필요한 클래스가 메모리에 올라가지 않아, 메모리 효율성이 좋아지고 프로그램 실행 속도도 빨라짐
3. 로딩(Loading) → 링킹(Linking) → 초기화(Initialization)

클래스 로더가 .class 파일을 JVM 메모리에 로드하면, 다음과 같은 단계를 거칩니다.
Loading
- .class파일의 바이트 코드를 읽어, Method Area(메서드 영역) 에 저장
Linking
클래스가 실행 가능한 상태가 되도록 준비하는 과정
Verification(검증) : 클래스 파일이 JVM 명세에 맞게 작성되었는지 확인 → 보안성 확보
Preparation(준비) : static 변수를 위한 메모리 공간을 확보하고 기본값으로 초기화
Resolution(해결) : Constant Pool 에 있는 심볼릭 레퍼런스를 실제 메모리 주소로 변환
Initialization
static 변수에 사용자가 지정한 값 할당
static 블록 실행
이 단계들이 끝나야 클래스가 실행될 준비를 마치게 됩니다.
4. 실행 엔진(Execution Engine)
로드 및 초기화된 클래스의 바이트 코드는 최종적으로 실행 엔진(Execution Engine)에 의해 실행됩니다.
하지만 CPU는 바이트코드를 직접 이해할 수 없기 때문에 기계어 변환 과정이 필요합니다.
JVM은 두가지 방식을 함께 사용합니다.
인터프리터 (Interpreter)
바이트코드를 한줄씩 해석하고 실행
빠른 시작
같은 코드가 반복될 때마다 매번 해석 → 느림
JIT 컴파일러 (Just-In-Time Compiler)
실행 중에 자주 호출되는 메서드를 네이티브 코드로 변환하고 캐싱
한 번 변환되면 이후에는 기계어 그대로 실행 → 빠름
컴파일 과정 자체에 시간이 걸림 (초기 오버헤드 발생)
→ JVM은 처음에는 인터프리터로 실행하고 반복되는 부분은 JIT로 변환하는 하이브리드 방식을 사용하며, 빠른 시작과 높은 성능을 동시에 얻습니다.
5. 실행 환경(JRE와 JVM)
JRE (Java Runtime Environment)
자바 실행에 필요한 기본 라이브러리 + JVM 으로 구성
컴파일된 프로그램을 실행할 수 있는 환경을 제공
JVM (Java Virtual Machine)
바이트코드를 실행하는 가상머신
운영체제 위에서 동작하면서, OS와 프로그램 사이에서 중개자 역할 수행