개발 지식

[개발 지식] JVM의 동작 원리와 구조

진철 2025. 2. 15. 20:21
728x90
반응형
들어가며

Java의 가장 큰 특징 중 하나는 어떤 운영 체제에서도 동일한 input을 넣으면 동일한 output이 나온다는 것입니다.

이는 JVM에 의한 것이라고 할 수 있습니다.

즉, 각 운영체제마다 JVM이 존재하기 때문에 운영 체제에 구애받지 않는 것이죠.

이번 시간에는 JVM의 동작 원리와 구조에 대해서 간단하게 알아보겠습니다.

 

JVM의 개요

JVM(Java Virtual Machine)은 자바 애플리케이션을 실행하기 위한 가상 머신입니다. 
JVM은 플랫폼 독립성을 제공하며, 바이트코드를 실행 가능한 기계어로 변환하여 각 운영 체제에서 동일한 실행 결과를 보장합니다. 

JVM은 JDK, JRE와 함께 자바 실행 환경을 구성하며, 각각의 역할은 다음과 같습니다.

JDK(Java Development Kit)
- 자바 개발 환경으로, 자바 애플리케이션을 개발하기 위해 필요한 도구를 제공합니다.
- 자바 소스를 바이트 코드로 변환하는 자바 컴파일러(`javac`), 자바 클래스 파일을 해석하는 역어셈블러(`javap`)*등을 포함합니다.

JRE(Java Runtime Environment)
- 자바 실행 환경으로, JVM, 자바 클래스 라이브러리, 기타 실행에 필요한 파일을 포함합니다.

JVM(Java Virtual Machine)
- 자바 애플리케이션을 실행하는 가상 머신입니다.
- 실제 컴퓨터로부터 실행을 위한 메모리를 할당받아 Runtime Data Area를 구성합니다.
- 인터프리터와 JIT(Just-In-Time) 컴파일러를 통해 바이트 코드를 기계어로 변환하고, 가비지 컬렉터(GC)를 사용하여 동적 메모리를 관리합니다.

 

JVM의 실행 과정

JVM은 다음과 같은 단계를 거쳐 프로그램을 실행합니다.

아래의 사진을 보면서 차근차근 따라가시면 이해하기 용이할 것입니다.



1. 클래스 로딩(Class Loading)
JVM은 실행 시 필요한 `.class` 파일을 클래스 로더(Class Loader)를 통해 메모리에 로드합니다.
- Bootstrap Class Loader: `java.lang` 패키지 등의 핵심 클래스를 로드.
- Platform Class Loader: Java SE Platform API의 클래스 로드.
- System Class Loader: 사용자가 작성한 애플리케이션 클래스 로드.

클래스 로딩 과정은 다음 단계로 나뉩니다.
1. Loading: `.class` 파일을 메모리에 로드.
2. Linking: 
   - Verify: `.class` 파일이 유효한지 검증.
   - Prepare: 클래스 변수에 기본값 할당.
   - Resolve: 심볼릭 레퍼런스를 실제 메모리 주소로 변환.
3. Initialization: static 필드 및 static 블록 실행.

2. 실행 엔진(Execution Engine)
클래스 로더를 통해 로드된 바이트코드는 실행 엔진을 통해 기계어로 변환됩니다.

실행 방식에는 두 가지가 있습니다.

따라서, Java는 인터프리터 언어이기도 하면서 동시에 컴파일러 언어인 것입니다.
- 인터프리터(Interpreter): 바이트코드를 한 줄씩 읽어 실행.
- JIT(Just-In-Time) 컴파일러: 실행 빈도가 높은 코드를 네이티브 코드로 변환하여 캐시하고, 재사용하여 성능을 최적화.

3. 런타임 데이터 영역(Runtime Data Area)
JVM은 실행 중 다양한 메모리 영역을 사용합니다.
- Heap: 객체와 배열이 저장되는 공간. 가비지 컬렉터가 관리.
- Method Area: 클래스 메타데이터, 정적 변수, 런타임 상수 풀이 저장됨.
- PC Register: 현재 실행 중인 명령어의 주소 저장.
- Stack: 메서드 호출 시 생성되는 프레임을 저장.
- Native Method Stack: 자바 외의 네이티브 코드를 실행할 때 사용.

4. 가비지 컬렉션(GC, Garbage Collection)
GC는 사용되지 않는 객체를 자동으로 수거하여 메모리를 해제하는 기능을 수행합니다.
- Mark 단계: 사용되지 않는 객체를 식별.
- Sweep 단계: 필요 없는 객체를 메모리에서 제거.
- Compact 단계: 메모리를 정리하여 단편화를 방지.

GC 최적화를 위해 다음과 같은 방법을 사용할 수 있습니다.
- 객체 재사용: StringBuilder 같은 가변 객체 활용.
- 메모리 누수 방지: 컬렉션 사용 후 `remove()` 호출.
- 객체 범위 제한: 지역 변수 활용으로 GC 부담 줄이기.

 

마치며

오늘은 이렇게 JVM의 작동 과정에 대해서 알아보았습니다.

꽤나 복잡하기도 하지만, 해당 개념을 잘 숙지하면 Java의 기본적인 문법에서 왜 오류가 발생하는지를 더 잘 파악할 수 있기에 알아두시면 좋겠습니다.

728x90
반응형