JVM이란
JVM은 기본적으로 자바 가상 머신 기반 프로그램이 linux, windows 등 어느 환경에서도 원활하게 돌아갈 수 있도록 돕는 환경이다.
JVM은 프로그램의 메모리를 관리하고 최적화하며 특정 기기상에서 실행되고 있는 프로세스, 자바 앱에 대한 리소스를 대표하고 통제하는 서버이다.
JVM의 특징
자바의 모토가 "Write once, run anywhere(WORA)"인 만큼, JVM을 통해 OS에 종속적이지 않은 애플리케이션을 개발할 수 있다. 자바 언어 자체는 컴파일을 통해 바이트코드로 변환되고 JVM을 통해 실행되기 때문에, 개발자는 코드 작성 시에 하드웨어 호환이나 OS호환에 대해 고려할 필요 없이 JVM 호환이 되는 언어나 자바로 코드를 작성하면 된다는 큰 장점이 있다.
JVM은 자바 환경에서의 메모리 관리, 보안, 플랫폼 자유도 지원도 수행하는데, 이는 추후에 자세히 알아보도록 하자.
자바의 컴파일 과정을 통해 JVM이 어떤 역할을 하는지 알아보자.
Java에서의 파일 실행 과정
전체적인 JVM의 자바 코드 실행 과정은 아래와 같다.
- JVM이 OS로부터 메모리 할당받음
- JVM의 컴파일러(javac)가 자바 소스코드(.java)를 바이트 코드(.class)로 컴파일 함
- JVM의 클래스로더가 동적 로딩을 통해 필요한 자바 클래스 로딩 및 링크를 수행하고 런타임 데이터 영역(Runtime Data Area: 실질적인 메모리를 할당받아 관리하는 영역) 에 올림
- 런타임 데이터 영역에 올라간 자바 바이트 코드(.class)들은 실행 엔진(Execution Engine)에 의해 해석됨
- 실행 엔진에 의해 Garbage Collector 작동 및 Thread 관리가 이루어짐
실행 엔진의 바이트 코드 해석 방식
4번 과정에서 런타임 영역의 바이트 코드를 해석하고 실행하는 과정에서 인터프리터, JIT 컴파일러 두 가지를 혼합하여 바이트 코드를 실행한다.
1. 인터프리터
- 바이트 코드 명령어를 하나씩 읽어서 해석하고 바로 실행
- JVM 내에서 기본적으로 인터프리터 방식으로 실행엔진이 바이트 코드를 실행함
- 단점: 같은 메서드라도 여러번 수행되면 매번 읽어야 함
2. JIT 컴파일러
- 반복적인 코드를 반복수행하지 않고 Native Code로 컴파일 해서 캐싱된 Native Code를 직접 실행하는 방식
- 명령어 하나씩 인터프리팅 해서 실행하는 것이 아니라 컴파일한 Native Code를 실행하는 것이기 때문에 전체적인 실행속도는 더 빠름
- Native Code(C, C++, 어셈블리어와 같이 자바의 부모가 되는 언어의 코드)로 컴파일 하는 데에도 비용이 들기 때문에 조건 충족하는 필요한 코드만 Native Code로 따로 컴파일 해서 실행(예: 호출 및 실행 빈도가 잦은 코드)
개발자가 작성한 소스코드는 .java파일로 저장된다. 저장된 .java파일은 컴파일러를 통해 이진코드(Binary Code)인 바이트코드(ByteCode), .class파일로 변환된다. 일반적으로 사용되는 컴파일러에는 JDK에 내장되어 있는 javac가 있다. 변환된 바이트코드(.class파일)는 JVM에서 실행가능하다. 전체적인 자바 개발 과정을 컴파일 과정부터 살펴보자.
컴파일러
컴파일러의 주요 역할은 앞서 말했던 JVM을 실행하는 것이다. 컴파일러는 개발자가 작성한 소스코드를 JVM 바이트코드라고 불리는 JVM이 이해가능한 자바 이진코드로 변환한다. 이 과정에서 컴파일러가 작성된 해당 소스코드에 구문 / 문법 오류가 있는지도 체크해준다. 이 때 구문 / 문법상의 오류가 존재하면 에러가 발생한다.
변환된 바이트코드 파일들은 클래스로더(Class Loader)를 통해 런타임 데이터 영역(Runtime Data Area)에 동적으로 배치된다. JVM을 통해서 실행되는데, 최종적으로 바이트코드 파일 .class를 실행하기 위해서는 세개의 스테이지를 거친다.
1. Class Loader
1) Loading
2) Linking
3) Initializing
2. Verifying Bytecode
3. Execution
Class Loader
컴파일러를 통해 바이트 코드로 변환된 자바 클래스들은 JVM의 클래스로더에 의해 런타임 데이터 영역에 로딩(Loading) 된다.
단, 이 때 변환된 클래스가 한 번에 로딩되는 것이 아니라 애플리케이션에서 필요로 하는 경우에만 동적으로 로딩된다.
런타임 데이터 영역(Runtime Data Area)
- JVM이 운영체제(OS) 위에서 실행되면서 할당받는 메모리 영역/자바 애플리케이션 실행 시에 사용되는 데이터들을 적재하는 영역
- 5가지 영역으로 나누어짐 (힙 영역, 스택 영역, 네이티브 메서드 스택, 메서드 영역, PC 레지스터)
- 메서드와 힙 영역은 런타임 데이터 영역 내에서 모든 스레드가 공유하는 영역이지만, PC 레지스터, 네이티브 메서드 스택, JVM 스택 영역은 스레드마다 생성되는 개별 영역이다.
JVM의 메모리 관리
- 클래스 로더를 통해 자바 애플리케이션을 읽어들임(클래스로더: JVM이 런타임 시 처음으로 클래스 참조할 때 해당 클래스를 메모리 영역에 배치시킴. 동적 로드를 담당하는 것이 클래스로더)
1. 메서드 영역(Method Area)
- JVM이 시작될 때 생성되는 공간
- 클래스 로더가 변환된 바이트 코드(.class)를 런타임 데이터 영역에 적재한다고 앞서 설명했는데, 초기화 되는 대상을 저장하는 영역이다. 바이트 코드를 처음 메모리에 올릴 때 메서드 영역에 적재된다.
- 아래와 같은 초기화 코드 정보가 저장된다
1) 필드 정보: 멤버 변수 이름, 데이터 타입, 접근 제어자 정보
2) 메서드 정보: 메서드 이름, return 타입, 함수 매개변수, 접근 제어자 정보
3) 타입 정보: Class or Interface, Type의 속성, Super Class의 이름
- Class Area / Static Area라고도 불리고 프로그램이 종료될 때 까지 여기에 저장됨
2. 힙 영역(Heap Area)
- 모든 스레드가 공유하는 영역이고, 런타임 시 동적으로 할당됨
- new 연산자로 생성되는 클래스, 인스턴스 변수, 배열 타입 등 Reference Type이 모두 저장됨
- 객체가 더 이상 사용되지 않거나 명시적으로 null 선언 시 GC가 처리함
- Reference Type이 저장되기 때문에 스택 변수나 다른 객체의 필드에서 참조되는데, 이 때 힙에 객체(데이터) 자체가 저장되고 스택에 힙의 참조주소를 저장하게 된다. 만약에 참조주소가 사라지면 GC가 이를 인식하여 객체를 힙에서 제거하는 것이다.
3. JVM 스택 영역(JVM Stack Area)
- 스택은 메서드가 실행되는 과정에서 생성되는 임시 데이터를 저장하기 위한 저장소라고 생각하면 된다.
- 하나의 메서드 호출 때마다 스택 프레임이라는 저장소를 생성한다.
- 스택은 LIFO 구조로 이루어져 있으며, 메서드가 호출될 때마다 그 안에서 사용되는 값을 저장하고 호출된 메서드의 매개변수, 지역변수, 리턴 값 및 연산 값을 임시로 저장한다. 메서드가 종료되면 프레임 또한 삭제된다.
- primitive type은 스택에 직접 값을 가지고, reference type은 힙에 객체가 저장되고 스택에 객체 주소를 저장한다.
- 프로세스가 로드 될 때 스택 사이즈는 고정되어 있는데, 프로그램 실행 중에 스택 메모리가 충분하지 않으면 StackOverFlowError가 발생한다.
4. PC 레지스터
- 각 스레드마다 JVM 내의 현재 실행 중인 명령어의 주소를 저장하는 메모리 공간을 의미한다.
- 스레드간 전환이 발생할 때 전환되는 스레드의 PC 레지스터 값이 로딩되어 다음 명령어가 실행된다.
- PC 레지스터로 인해 다중 스레드 환경에서 동시에 여러 명령어 실행이 가능하다.
5. 네이티브 메서드 스택
- 자바 언어 이외의 언어로 작성된 코드를 실행하는 영역이다.
- 네이티브 메서드 호출 시에 해당 영역이 생성되고 실행이 완료되면 메모리에서 해제되는 영역이다.
클래스 로더에 클래스 파일 로딩 과정은 아래와 같다.
1. Loading(로드)
- 클래스로더가 애플리케이션이 필요로 하는 클래스 파일을 런타임 데이터 영역(메모리)에 적재
2. Linking(링크)
- 적재된 클래스 파일을 사용하기 위해 검증하는 과정
1) Verifying: 로드된 클래스가 JVM의 명세대로 구성되어 있는지 체크
2) Preparing: 클래스가 필요로 하는 메모리 할당
3) Resolving: 클래스 상수 풀 내의 모든 심볼릭 레퍼런스를 다이렉트 레퍼런스로 변경
3. Initialization(초기화)
- 클래스 변수들을 설정 값으로 초기화(static 변수와 같은 설정된 값이 존재하는 변수)
변환된 바이트 코드 파일들은 1번 로딩(Loading) 과정을 통해 JVM의 힙(Heap) 영역에 저장된다.
저장된 힙 영역에서 Linking-Initializing(로딩-연결-실행준비)단계를 통해 실행준비를 모두 마치게 된다.
실행준비가 끝난 Class Loader의 파일들은 Bytecode Verifier에 의해 실행하는데에 문제가 없는지 검증과정을 거치게 된다.
이 때, Bytecode Verifier는 아래와 같은 것들을 체크한다.
- 호출 변수 정의 여부 체크
- 메서드의 호출 객체 타입 체크
- 접근제어자 오류체크
- 런타임 스택 오버플로우 체크
- 지역변수가 런타임 스택에 포함되는지 여부 체크
만약 Bytecode Verifier를 통한 검증 단계에서 하나라도 문제가 발생한다면 Exception을 발생시키고 컴파일 프로세스를 중단시킨다.
여기까지의 과정에서 문제가 발생하지 않았다면 JVM의 실행 엔진(Execution Engine)은 바이트코드를 한줄 씩 읽어나가며 주어진 작업을 수행할 것이다.
Interpretation
바이트 코드 명령어를 한줄씩 읽어서 실행하는 방법이다. 개별 명령 수행 속도는 빠르나, 전체 실행속도는 느리다는 단점이 있다.
Just-In-Time Compiler
인터프리터의 비교적 느린 전체 실행속도를 보완하기 위한 방식으로 바이트코드 전체를 컴파일하여 바이너리 코드로 만든다. 이후에는 메서드를 인터프리팅 하지 않고, 컴파일된 바이너리 코드를 직접 실행하는 방식이다. 코드 한줄 씩 인터프리팅 하는 방식이 아니기 때문에 전체적인 실행속도는 빠르다.
References
https://gyoogle.dev/blog/computer-language/Java/Java%20Virtual%20Machine.html
[Java] 자바 가상 머신(Java Virtual Machine) | 👨🏻💻 Tech Interview
[Java] 자바 가상 머신(Java Virtual Machine) 시스템 메모리를 관리하면서, 자바 기반 애플리케이션을 위해 이식 가능한 실행 환경을 제공함 JVM은, 다른 프로그램을 실행시키는 것이 목적이다. 갖춘 기
gyoogle.dev
https://www.prepbytes.com/blog/java/java-compilation-process/
Java Compilation Process
The source code of a Java code is compiled into an intermediate binary code known as the Bytecode during the Java compilation process.
www.prepbytes.com
https://www.theserverside.com/definition/Java-compiler
What is a Java compiler? | Definition from TechTarget
A Java compiler takes a text file and compiles it into a platform-independent Java file, meaning the compiled code can run on any OS. Learn how it works.
www.theserverside.com
https://medium.com/javarevisited/how-java-code-compiled-and-run-e4702fb83ffa
How Java Code Compiled And Run ?
Short story about Java compiling process
medium.com
https://www.geeksforgeeks.org/compilation-execution-java-program/
Compilation and Execution of a Java Program - GeeksforGeeks
A Computer Science portal for geeks. It contains well written, well thought and well explained computer science and programming articles, quizzes and practice/competitive programming/company interview Questions.
www.geeksforgeeks.org
☕ JVM 내부 구조 & 메모리 영역 💯 총정리
저번 포스팅에서는 JRE / JDK / JVM에 대해서 간략하게 알아보는 시간을 가졌다면, 이번 포스팅에서는 JVM의 내부 구조에 대해 좀 더 자세하게 알아보도록 할 예정이다. JVM(자바 가상 머신)은 자바 언
inpa.tistory.com
[Java] JVM(Java Virtual Machine) 이해하기 -2 : 메모리 영역(Runtime Data Area)
해당 글에서는 JVM의 구성요소 중 하나인 Runtime Data Area에 대해 상세하게 알아보기 위해 작성한 글입니다. 💡 JVM의 동작과정에 대해 궁금하시면 아래의 글을 참고하시면 크게 도움이 됩니다. [Java
adjh54.tistory.com
'Java' 카테고리의 다른 글
[Java] 문자열 클래스 (0) | 2024.02.03 |
---|---|
[Java] 직렬화(Serialization) (0) | 2024.01.31 |
[Java] 오토박싱(Autoboxing) & 오토 언박싱(Autounboxing) (0) | 2024.01.25 |
[Java] Primitive Type & Reference Type (0) | 2024.01.25 |
[Java] Call by value & Call by reference (0) | 2024.01.25 |