티스토리 뷰
JVM이란?
JVM이란 Java Virtual Machine의 약자이다. 즉, 자바 애플리케이션을 실행하기 위한 가상 머신이다. JVM은 컴파일된 자바의 소스코드를 읽어 들이고, Java API를 통하여 운영체제와 Java의 중간자 역할로써 운영체제마다 다른 system call을 사용하여 운영체제가 다르더라도 동일한 연산을 수행할 수 있다.
자바 프로그램의 실행 과정은 다음과 같다.
- Java 컴파일러는 소스코드(.java)를 읽어서 바이트 코드(.class)로 변환한다.
- Class Loader는 변환된 바이트 코드(.class)를 JVM의 Runtime Data Area로 로딩한다.
- Exeuction Engine은 로딩된 바이트 코드(.class)를 해석한다. (해석하는 방법에는 Interpreter와 JIT Compiler 두 가지 방식이 있다.)
- 필요에 따라 Thread Synchronization과 Garbage Collection이 이루어진다.
Class Loader (클래스 로더)
Java는 동적으로 클래스를 읽어오므로, 프로그램이 실행되는 런타임에서야 프로그램의 모든 코드가 JVM의 Runtime Data Area로 로딩&링킹 된다. 이러한 역할을 하는 것이 Class Loader이다.
Runtime Data Area (런타임 데이터 영역)
Runtime Data Area는 JVM이 운영체제 위해서 프로그램을 실행하기 위해 할당받는 메모리 영역이다. 런타임 데이터 영역은 다음과 같이 5가지 영역으로 나뉠 수 있다.
- 스레드가 공유하는 영역
- Heap
- new 명령어로 생성된 모든 객체와 배열이 저장되는 공간. Garbage Collection의 대상이 되는 공간이다.
- Method
- 클래스의 정보, 변수의 이름이나 타입, 메소드의 이름이나 리턴 타입, static 변수, 상수 등이 저장되는 공간.
- Heap
- 스레드가 공유하지 않는 영역 (= 스레드마다 가지고 있는 영역)
- PC Register
- CPU의 Program Counter와 유사하며, JVM의 현재 인스트럭션의 주소가 저장된다.
- Stack
- 각 메소드가 실행될 때마다 스택 프레임이 생성되는 스택 영역이다. 메소드가 실행하면서 사용하는 매개변수, 지역변수, 리턴 값 등이 저장되며, 메소드 실행이 끝나면 스택 프레임은 이 영역에서 제거된다.
- Heap 영역에 있는 객체를 가리키는 레퍼런스 변수가 Stack에 저장된다.
- Native Method Stack
- Java가 아닌 다른 언어로 작성된 코드를 위한 공간이다. JNI(Java Native Interface)를 통해 다른 언어의 코드를 실행하여 커널에 접근할 수 있다.
- PC Register
Execution Engine (실행 엔진)
클래스 로더를 통해 JVM의 런타임 데이터 영역에 배치된 바이트 코드는 Exeuction Engine에 의해 실행된다. Execution Engine은 바이트 코드를 명령어 단위로 읽어서 실행한다. 하지만 바이트 코드는 기계가 바로 수행할 수 있는 언어보다는 비교적 인간이 보기 편한 형태이기 때문에, Execution Engine은 바이트 코드를 실제로 JVM 내부에서 기계가 실행할 수 있는 기계어로 번역하며, 방식은 다음과 같이 두 가지가 있다.
- Interpreter (인터프리터)
- 바이트 코드의 인스트럭션을 한 줄씩 읽어서 해석하고 실행한다.
- 한 줄씩 해석하고 실행하기 때문에 바이트 코드의 각 명령어의 실행은 빠르지만, 프로그램 전체의 실행 결과는 느리다는 단점을 가지고 있다.
- 바이트 코드는 기본적으로 인터프리터 방식으로 동작한다.
- JIT(Just-In-Time) Compiler
- 인터프리터의 단점을 보완하기 위해 도입되었다. 인터프리터 방식으로 실행하다가 적절한 시점에 바이트 코드 전체를 컴파일하여 기계어로 번역하고, 이후에는 해당 바이트 코드를 더 이상 번역하지 않고 기계어로 직접 실행하는 방식이다.
- 기계어를 실행하는 것이 한 줄씩 인터프리팅 하는 것보다 빠르고, 기계어는 캐시에 보관하기 때문에 한 번 컴파일된 코드는 빠르게 수행된다.
실행 엔진에서 메모리를 효율적으로 관리하기 위해 Garbage Collection을 실행한다.
Garbage Collection
Garbage Collection은 메모리 누수와 같이 프로그램이 필요하지 않은 메모리를 계속 점유하고 있는 상황에서 Garbage Collector가 주기적으로 메모리를 해제하는 행위이다. 이때 Garbage Collection의 대상이 되는 영역은 Heap 영역이다. 따라서 개발자가 따로 메모리를 관리하지 않아도 된다.
Garbage Collection을 수행하는 과정을 Mark & Sweep이라고 부른다. 다음의 과정을 살펴보자.
- 메소드 하나가 실행되면서, Stack과 Heap에 필요한 정보를 저장한다. (예: 변수, 동적 할당)
- 실행 도중, 더 이상 사용하지 않는 메모리 공간이 발생한다.
- GC(= Garbage Collector)는 Stack 영역을 스캔하면서 현재 Stack 영역의 변수가 참조하고 있는 Heap 영역의 부분을 표시한다. 만약 Heap 영역의 객체가 다른 객체를 참조하면서 사슬이 형성되면, 참조하는 모든 객체를 표시한다. (Mark)
- GC는 Heap 영역을 스캔하면서 표시되지 않은 부분의 메모리를 해제한다. (Sweep)
Mark & Sweep을 수행하기 전에 Garbage Collector를 제외한 모든 스레드는 작업을 중단한다. 이를 Stop the world라고 부르며, GC의 작업이 끝나면 중단했던 작업을 다시 시작한다. 따라서 GC는 성능과 관련된 이슈를 가지고 있다.
본 포스트는 다음 주소를 참고하였습니다.
'프로그래밍 언어 > Java' 카테고리의 다른 글
[Java] Java 8 변경사항 (0) | 2020.02.09 |
---|---|
[Java] Java란? (0) | 2020.01.29 |
- Total
- Today
- Yesterday