오늘은 지난 글(2022.12.17 - [언어] - [Java] static)에 이어서 JVM에 대해 자세히 알아보려고 한다.
아래 정리가 잘 되어 있는 블로그 글의 내용을 정리했습니다.
[JAVA] ☕ JVM 내부 구조 & 메모리 영역 자세히 정리
저번 포스팅에서는 JRE / JDK / JVM에 대해서 간략하게 알아보는 시간을 가졌다면, 이번 포스팅에서는 JVM의 내부 구조에 대해 좀 더 자세하게 알아보도록 할 예정이다. JVM(자바 가상 머신)은 자바 언
inpa.tistory.com
JVM 이란?
JVM (Java Virtual Machine)은 자바 바이트코드를 실행할 수 있는 주체를 말한다. 모든 자바 프로그램은 JVM을 통해 CPU나 운영체제의 종류와 상관 없이 동일하게 동작할 것을 보장해 준다. - wikipedia
그렇다면 위에 정의되어 있는 자바 바이트코드는 무엇인가? 일반적인 소스코드는 .java 확장자로 되어있는 파일을 말하는데, 이는 인간이 사용하는 프로그래밍 언어로 작성되어 있다. 예시는 아래와 같다.
위 소스코드를 컴퓨터가 그대로 이해하면 좋겠지만, 컴퓨터는 1과 0으로 이루어진 코드를 해석하기 때문에 그에 맞는 파일로 변환을 해주어야하는데 그 과정이 바로 컴파일이고 결과물이 바로 바이트코드인 것이다.
javac 명령어로 컴파일 해주면 .class 확장자 파일이 생성되는데 이를 바이트 코드라고 한다. JVM에 의해 실행되는 파일인 것이다.
JVM의 동작 방식
JVM은 자바 애플리케이션을 클래스 로더를 통해 읽어 자바 API와 함께 실행한다.
- 자바 애플리케이션을 실행하면 JVM은 운영체제로부터 메모리를 할당 받는다.
- 자바 컴파일러가 자바 소스코드(.java 파일)를 자바 바이트코드(.class)로 컴파일한다.
- Class Loader는 동적 로딩을 통해 필요한 클래스들을 로딩 및 링크하여 Runtime Data Area에 올린다.
- Runtime Data Area에 로딩된 바이트코드는 Execution Engine을 통해 해석된다.
- 이 과정에서 Execution Engine에 의해 Garbage Collector의 작동과 Thread 동기화가 이루어진다.
JVM의 구조
위 그림에 일부 나타냈듯이 JVM은 아래의 목록으로 구성되어있다.
- 클래스 로더
- 실행 엔진
- 인터프리터
- JIT 컴파일러
- 가비지 콜렉터(GC)
- 런타임 데이터 영역
- 메소드영역
- 힙영역
- PC REGISTER
- 스택 영역
- 네이티브 메소드
- JNI - 네이티브 메소드 인터페이스
- 네이티브 메소드 라이브러리
1. 클래스 로더
클래스 로더는 JVM 내로 자바 바이트코드를 동적으로 로드하고, 링크를 통해 배치하는 작업을 수행하는 모듈이다. 즉, 바이트코드를 JVM 메모리 영역인 Runtime Data Area에 배치한다.
클래스를 메모리에 올리는 로딩 기능은 한번에 메모리에 올리지 않고, 애플리케이션에서 필요한 경우 동적으로 메모리에 적재한다.
자바 클래스 로딩 순서는 Loading → Linking → Initailzation 3단계로 이루어진다.
- Loading(로드) : 컴파일되어 바이트코드로 변환된 내용을 JVM 메모리에 로드한다.
- Linking(링크) : 파일을 사용하기 위해 검증하는 과정이다.
- Verifying(검증) : 읽어들인 클래스가 JVM 명세에 명시된 대로 구성되었는지 검사한다.
- Preparing(준비) : 클래스가 필요로 하는 메모리를 할당한다.
- Resolving(분석) : 클래스의 상수 풀 내 모든 심볼릭 레퍼런스를 다이렉트 레퍼런스로 변경한다.
- Initialization(초기화) : 클래스 변수들을 적절한 값으로 초기화한다. (static 필드를 설정된 값으로 초기화하는 등)
2. 실행 엔진(Execution Engine)
실행 엔진은 클래스 로더를 통해 런타임 데이터 영역에 배치된 바이트 코드를 명령어 단위로 읽어서 실행한다.
이 수행 과정에서 실행 엔진은 인터프리터와 JIT 컴파일러 두 가지 방식을 혼합해 바이트 코드를 실행한다.
인터프리터
바이트 코드 명령어를 하나씩 읽어서 해석하고 실행한다.
JVM 안에서 바이트코드는 기본적으로 인터프리터 방식으로 동작한다. (관련 내용:2022.11.29 - [언어] - 컴파일 언어 vs 인터프리터 언어)
JIT 컴파일러(Just In Time Compiler)
위 인터프리터의 단점을 보완하기 위해 도입된 방식으로 반복되는 코드를 발견해 바이트 코드 전체를 컴파일하여 Native Code로 변경하고 이후에는 해당 메서드를 더이상 인터프리팅 하지 않고 캐싱해 두어 네이티브 코드로 직접 실행하는 방식이다.
가비지 컬렉터
JVM은 가비지 컬렉터를 이용해 Heap 메모리 영역에서 더는 사용하지 않는 메모리를 자동으로 회수해 준다.
C언어 같은 Native 언어 같은 경우 직접 메모리를 해제해줘야 하지만, JAVA는 이를 통해 쉽게 메모리 최적화가 가능하다.
자세한 내용은 다음 글에서 다뤄보겠다.
3. 런타임 데이터 영역
런타임 데이터 영역은 JVM 메모리 영역으로 자바 애플리케이션을 실행할 때 사용되는 데이터들을 적재하는 영역이다.
이전 static을 다룰 때 간략하게 소개했던 내용이기 때문에 넘어간다. (참고 : 2022.12.17 - [언어] - [Java] static)
4. JNI (Java Native Interface)
JNI는 자바가 다른 언어로 만들어진 애플리케이션과 상호 작용할 수 있는 인터페이스를 제공하는 프로그램이다.
Native Method를 적재하고 수행할 수 있도록 한다. 하지만 실질적으로 제대로 동작하는 언어는 c / c++ 정도 밖에 없다고 한다.
5. Native Method Library
c, c++로 작성된 라이브러리를 말한다.
만일 헤더가 필요하면 JNI는 이 라이브러리를 로딩해 실행한다.
'언어' 카테고리의 다른 글
[Java] 메모리 누수 원인 알아보기 (0) | 2023.01.01 |
---|---|
[Java] static (0) | 2022.12.17 |
[Java] Java8 - 함수형 프로그래밍(5) (0) | 2022.12.04 |
[Java] Java8 - 함수형 프로그래밍 (4) (0) | 2022.12.01 |
컴파일 언어 vs 인터프리터 언어 (0) | 2022.11.29 |