JVM이란?
- Java Virtual Machine
- 자바 가상 머신
- 자바와 운영체제 사이에서 중개자 역할을 수행
- 자바가 운영체제에 구애 받지 않고 프로그램을 실행할 수 있도록 도와줌
- 가비지 컬렉터를 사용한 메모리 관리도 자동으로 수행
- 레지스터 기반이 아닌 스택 기반으로 동작
[장점]
JVM을 사용하면 하나의 바이트 코드(.class)로 모든 플랫폼에서 동작
💡 바이트 코드(bytecode)란?
특정 하드웨어가 아닌 Virtual Machine에서 돌아가는 실행 프로그램을 위한 이진 표현법으로 하드웨어가 아닌 소프트웨어에 의해 처리되기 때문에 기계어보다 추상적이다.
JVM 구조
1. Class Loader
- loading
- linking
- initialization
2. Runtime Memory/Data Area
- method
- heap
- PC register
- stack
- native method stack
3. Execution engine
4. Java Native Interface (JNI)
5. Native Method Libraries
클래스 로더가 컴파일 된 자바 바이트 코드를 메모리 영역에 로드하고, 실행 엔진이 자바 바이트 코드를 실행
1. Class Loader
- Loading (로딩)
- Linking (링크)
- Initialization (초기화)
JVM 내로 클래스를 로드하고, 링크를 통해 배치하는 작업을 수행하는 모듈
1. Loading (로딩)
- Bootstrap Class Loader: 루트 클래스 로더, java.net, java.util 등과 같은 표준 Java 패키지를 로드, Java_HOME\lib에 있는 코어 자바 API를 제공
- Extension Class Loader: JAVA_HOME\lib\ext 폴더 또는 java.ext.dirs 시스템 변수에 해당하는 위치에 있는 클래스를 읽음
- Application Class Loader: 클래스 경로에 있는 파일을 로드, -classpath 또는 -cp 명령 옵션을 추가하여 클래스 경로 수정 가능
클래스 로더가 .class 파일을 릭고 그 내용에 따라 적절한 바이너리 데이터를 만들어 메소드 영역에 저장
로딩이 끝나면 해당 클래스 타입의 Class 객체를 생성하여 힙(heap) 영역에 저장
2. Linking (링크)
- Verification (검증): .class 파일 형식이 유효한지 체크
- Preparation (준비): 클래스 또는 인터페이스의 static 영역에 메모리를 할당하고 기본값으로 초기화
예시)
private static final boolean enabled = true;
boolean 기본 값이 false 할당
- Resolution (해결): 심볼릭 메모리 레퍼런스를 메모리 영역에 있는 실제 레퍼런스로 교체
3. Initialization (초기화)
private static final boolean enabled = true;
준비 단계에서 false로 기본값이 할당 되었고 초기화 단계에서는 실제 값인 true가 할당
2. Runtime Data Area/Memory
- Method Area
- Heap Area
- Stack Area
- PC Register
- Native Method Stack
1. Method Area
run-time 상수 풀, 필드, 메소드 데이터, 메소드 및 생성자에 대한 코드와 같은 모든 클래스 수준 데이터가 여기에 저장
public class Employee {
private String name;
private int age;
public Employee(String name, int age) {
this.name = name;
this.age = age;
}
}
name과 age 및 constructor 세부정보가 method 영역에 저장
2. Heap Area
모든 객체와 인스턴스 변수가 저장됨
모든 클래스 인스턴스와 배열에 대한 메모리가 할당되는 런타임 데이터 영역
Employee employee = new Employee();
heap 영역에서 Employee의 인스턴스가 생성되고 로드됨
heap은 virtual machine이 시작될 때 생성되며 JVM 당 하나의 힙 영역이 존재
3. Stack Area
모든 로컬 변수, 메소드 호출, 리턴 값이 스택 영역에 저장
모든 메소드 호출마다 각각 스택 프레임이라고 하는 스택메모리가 생성됨
메소드 호출이 완료 되면 스택 프레임 파괴
스택 프레임은 3가지 하위 부분으로 나뉨
- Local Variables: 모든 지역 변수와 해당 값 저장
- Operand Stack: 피연산자 스택, LIFO 스택 포함
- Frame Data: 모든 기호 저장
4. PC(Program Counter) Registers
JVM은 멀티 스레드를 지원
스레드가 시작될 때 생성, 스레드마다 하나씩 존재
현재 스레드가 실행되는 부분의 주소와 명령을 저장하고 있는 영역
5. Native Method Stacks
JVM에는 기본 메소드를 지원하는 스택이 포함되어 있음
이러한 메소드는 C, C++ 같은 자바가 아닌 다른 언어로 작성되어 있음
다른 언어로 작성된 네이티브 코드를 위한 메모리 영역으로 C, C++ 코드를 수행
3. Execution Engine (실행 엔진)
- Interpreter
- JIT Compiler
- GC (Garbage Collector)
- Serial GC
- Parallel GC
- Garbage First(G1) GC
메모리에 로드된 바이트코드를 기계어로 변경하여 각 클래스에 있는 코드를 실행
1. Interpreter (인터프리터)
인터프리터는 한 줄씩 읽고 실행
한 줄 한 줄 실행하기 때문에 비교적 느림
메소드가 여러번 호출될 때 매번 새로운 해석이 필요
2. JIT Compiler
실행 엔진은 바이트코드를 실행하기 위해 먼저 인터프리터를 사용하지만 반복되는 코드를 발견할 경우 JIT 컴파일러를 사용
JIT 컴파일러는 전체 바이트코드를 컴파일하고 기계어로 번역
기계어는 반복되는 메소드 호출에 직접 사용되어 시스템의 성능을 향상
JIT 컴파일러 구성요소
1. Intermediate Code Generator - 중간 코드 생성
2. Code Optimizer - 중간코드 최적화
3. Target Code Generator - 중간코드를 기계어로 변환
4. Profiler - 핫스팟(반복적으로 실행되는 코드) 찾기
3. GC (Garbage Collector)
Garbage Collector는 힙 영역에서 참조 되지 않는 객체를 수집하고 제거
힙 메모리에서 참조 되지 않은 객체를 제거하고 새 객체를 위한 여유 공간을 만들기 때문에 JAVA 메모리를 효율적으로 만듦
1. Mark - GC는 메모리에서 사용되지 않는 객체를 식별
2. Sweep - 이전 단계에서 식별된 객체를 제거
Garbage Collections은 정기적으로 JVM에 의해 자동으로 수행되므로 별도로 처리할 필요 없음
System.gc()를 호출하여 트리거할 수도 있지만 실행 보장 안됨
JVM는 3가지 garbage collector가 있음
- Serial GC: 가장 간단하며 싱글 스레드 환경에 실행되는 작은 애플리케이션을 위해 설계됨. 전체 애플리케이션 중지. JVM argument: -XX:+UseSerialGC
- Parallel GC: GC의 디폴트. Throughput Collector라고도 함. 멀티스레드에서 사용하지만 실행 중에 애플리케이션을 일시 중지함. JVM argument: -XX:+ParallelGC
- Garbage First (G1) GC: 4GB 이상의 큰 힙 사이즈를 사용할 수 있는 멀티스레드 애플리케이션을 위해 설계됨. 힙을 동일한 크기의 집합으로 나누고 멀티스레드를 이용하여 스캔함. garbage가 많은 지역을 식별하고 그 곳을 먼저 garbage collection함. JVM argument: --XX+G1GC
4. Java Native Interface (JNI)
- 하드웨어와 상호작용하거나 Java의 메모리 관리 및 성능 제약을 극복해야 하는 경우 자바가 아닌 네이티브 코드(예: C,C++)를 사용해야될 수 있음
- 자바는 JNI를 통해 네이티브 코드 실행을 지원
- JNI는 C, C++ 등 다른 프로그래밍 언어에 대한 패키지를 지원하는 다리 역할
- Native Keyword를 사용한 메소드 호출
- System.loadLibrary()
5. Native Method Libraries
- 네이티브 메소드 라이브러리는 C, C++, 어셈블리와 같은 다른 프로그래밍 언어로 작성된 라이브러리
- .dll 또는 .so 파일 형식으로 제공
- JNI를 통해 로드
자바 프로그램 실행 과정
- 자바 컴파일러(javac)가 자바 소스코드(.java)를 읽어 자바 바이트코드(.class)로 변환한다.
- Class Loader를 통해 class 파일들을 JVM으로 로딩한다.
- 로딩된 class 파일들은 Execution Engine을 통해 해석된다.
- 해석된 바이트코드는 Runtime Data Area에 배치되어 실질적인 수행이 이루어진다.
참고 사이트
https://catsbi.oopy.io/df0df290-9188-45c1-b056-b8fe032d88ca#719bdb8b-71e0-43b8-a969-7bc7251f87fa
https://www.freecodecamp.org/news/jvm-tutorial-java-virtual-machine-architecture-explained-for-beginners/
https://doozi0316.tistory.com/entry/1%EC%A3%BC%EC%B0%A8-JVM%EC%9D%80-%EB%AC%B4%EC%97%87%EC%9D%B4%EB%A9%B0-%EC%9E%90%EB%B0%94-%EC%BD%94%EB%93%9C%EB%8A%94-%EC%96%B4%EB%96%BB%EA%B2%8C-%EC%8B%A4%ED%96%89%ED%95%98%EB%8A%94-%EA%B2%83%EC%9D%B8%EA%B0%80
https://helloworld-88.tistory.com/16