JVM이란?
JVM은 Java Virtual Machine의 줄임말로 자바 코드를 클래스 파일로 컴파일하여 컴퓨터에 읽어 들이고 메모리에 로드시켜 자바프로그램이 어떤 CPU나 운영체제에서도 실행될 수 있게 합니다. C언어는 윈도우에서 컴파일한 파일과 리눅스에서 컴파일한 파일이 동일하지 않아 다른 운영체제에서 컴파일한 파일은 동작하지 않습니다. 자바는 다릅니다. 자바 파일이 컴파일된 클래스 파일은 어떤 플랫폼에서도 동일하게 실행이 됩니다. 그래서 JAVA는 플랫폼에 종속적이지 않다는 말이 있습니다. 하지만 JVM은 플랫폼에 종속적입니다. 예를 들면 IBM이나 오라클과 같은 제조사마다 또는 리눅스와 윈도처럼 운영체제마다 다를 수 있습니다.
또, 자바 프로그램을 실행하기 위한 메모리를 컴퓨터에게 할당받아 클래스, 메서드, 변수 등에 메모리를 배정해주고 사용하지 않는 메모리는 다시 회수해 가기도 합니다.
JVM의 구조
JVM은 여러 구성요소가 있습니다. 이번 글에서는 자바 프로그램의 실행과정과 구성요소 중 런타임 데이터 영역(Runtime Data Area)에 대해서 알아보겠습니다.
● 클래스 로더(Class Loader)
● 런타임 데이터 영역(Runtime Data Area)
⊙ 메서드 영역(Method Area)
⊙ 힙 영역(Heap Area)
⊙ 스택 영역(Stack Area)
⊙ PC 레지스터(PC Registers)
⊙ 네이티브 메서드 스택(Native Method Stack)
● 실행 엔진(Execution Engine)
⊙ 인터프리터(Interpreter)
⊙ JIT 컴파일러(JIT Compiler)
⊙ 가비지 컬렉터(Garbage Collector)
● 네이티브 메서드 인터페이스(Native Method Interface)
● 네이티브 메서드 라이브러리(Native Method Libraries)
JVM의 자바 어플리케이션 실행 과정
1. 어플리케이션이 실행되면 JVM이 운영체제(OS)로부터 메모리를 할당받습니다. 할당받은 메모리는 용도에 따라 영역을 구분하여 관리됩니다.
2. 자바 컴파일러(javac.exe)가 자바 소스코드(.java)를 읽어서 자바 바이트 코드(. class)로 변환합니다.
3. Class Loader가 컴파일된 바이트 코드를 Runtime Data Area에 로드합니다.
4. 로딩된 바이트 코드는 Execution Engine에 의해서 해석됩니다.
5. 해석된 바이트 코드는 Runtime Data Area에 배치되어 실행되고 이 과정에서 Garbage Collector의 작동과 Thread동기화가 이루어집니다.
런타임 데이터 영역(Runtime Data Area)
런타임 데이터 영역은 JVM의 메모리에 데이터들을 적재하기 위해 OS에게 할당받은 공간입니다. 이 공간은 크게 5가지로 나뉘는데 각각 메서드 영역(Method Area), 힙 영역(Heap Area), 스택 영역(Stack Area), PC 레지스터(PC Registers), 네이티브 메서드 스택(Native Method Stack)이 되겠습니다.
메서드 영역(Method Area)
메서드 영역은 JVM 내에서 한 개만 존재하며 모든 스레드에서 공유하는 영역이고 클래스 파일이 로드될 때 초기화 되는 대상들을 저장합니다. 그리고 garbage collector의 관리 대상입니다. 메서드 영역 내에 Runtime Constant Pool이라는 별도 공간이 있는데 이곳에서 클래스에 대한 다양한 정보가 저장됩니다. 저장되는 정보의 종류는 다음과 같습니다.
- Field Info: 멤버 변수의 이름, 데이터 타입, 접근 제어자의 정보
- Method Info: 메서드의 이름, Return 타입, 매개변수, 접근제어자의 정보
- Type Info: Class인지 Interface인지 여부 저장, Type의 속성 이름, Super Class 이름
힙 영역(Heap Area)
힙 영역은 메서드영역과 마찬가지로 JVM 내에서 한 개만 존재하며 모든 스레드를 공유하는 영역이고 garbage collector 관리를 받습니다. 객체를 저장하기 위한 메모리 영역이며 new 연산자로 생성된 모든 객체와 배열이 저장되는 영역입니다. 스택영역의 참조변수가 new연산으로 생성되어 힙 영역에 위치한 객체를 가리키게 되며 스택의 변수에서 참조하는 위치가 바뀌어 해당 객체가 사용되지 않는 상태로 남겨진 경우 garbage collector에 의해 메모리에서 해제됩니다.
그림은 Heap 영역의 구성도입니다. Eden, Survivor, Old 영역이 있으며 Eden과 Survivor는 Young Generation이라는 영역으로 불립니다. 자바 객체가 생성되자마자 저장되고 주기가 짧은 객체들이 이 영역에 존재합니다.
Eden에서부터 new연산으로 객체가 생성되고 저장되어 참조 정도에 따라 survivor 0번 또는 1번으로 밀려나게 됩니다.
survivor영역 2개는 객체를 번갈아서 받는 게 아니라 한쪽은 비워 둔 상태로 다른 한쪽만 객체를 받다가 gc를 통해 영역을 비우고 다른 suvivor영역에서 객체를 받습니다. 새로운 객체가 계속 생겨 더 밀려나면 Old 영역으로 옮겨지게 됩니다. 이때 Young Generation영역에서 gc가 주기적으로 일어나는데 이는 Minor gc라고 합니다.
Old 영역은 객체가 생성된 지 오래되어서 Young Generation에서 밀려났거나 크키가 큰 객체들이 저장되는 영역입니다. 이 공간도 마찬가지로 메모리가 가득차게 되면 gc를 수행하게 되는데 Old영역은 Major gc라고 합니다. Major gc는 메모리 공간의 크키가 큰 Old영역의 모든 객체들을 검사해서 사용하지 않는 객체들을 삭제하게 됩니다. 작은 공간에서 비교적 빈번하게 일어나는 Minor gc와는 다르게 한번 실행하는데 많은 시간이 소요되고 모든 Thread를 중지시킨 상태로 작업하게 되어 Stop-the-World 현상이 발생합니다.
※ Stop-the-World란?
JVM이 gc를 위해서 자신을 제외한 모든 Thread를 멈추는 현상으로 garbage collector작업이 모두 종료된 후 중단됐던 작업들을 재개합니다.
스택 영역(Stack Area)
스택 영역은 메서드 내에서 정의하는 기본 자료형인 원시 타입(Primitive type: int, boolean, float, double 등)을 사용하는 지역변수와, 매개변수의 값이 저장되는 공간입니다. 스레드마다 독립적인 영역을 가지고 있고 메서드가 호출될 때 메모리가 할당됩니다.
- 메소드가 호출될 때 스택 영역 안에 스택 프레임이 생기고 그 메서드를 위한 지역변수, 매개변수 등의 연산에 필요한 값이 저장됩니다. 메서드 수행이 끝나면 저장되었던 값들은 사라집니다.
- 스택영역에서 선언한 값들은 스택영역의 스택프레임에 저장되지만 new 연산을 통해 생성한 새로운 객체는 스택영역에 저장되지 않습니다.
Student lee=new Student("Lee");
이런 식으로 새로운 Student 객체를 생성하면 객체를 참조하는 변수인 lee는 스택영역에 저장되지만 참조변수가 바라보는 객체는 스택영역이 아니라 Heap영역에 메모리가 저장됩니다.
PC 레지스터(PC Registers)
PC 레지스터는 현재 수행 중인 연산의 메모리 주소를 기억하는 장치입니다. 연산을 수행하는동안 데이터(Operand)와 어떤 연산을 할것인지를 말하는 명령어(Instruction)이 있습니다. 메모리 주소를 다음으로 옮겨가며 연산을 할때 데이터들을 저장해두어야 할 필요가 있는데 저장해두는 이 공간을 Register라고 하며 별도의 메모리 공간을 잡지 않고 Register에서 기억하는 이유는 비효율적이기 때문입니다.
- 스레드마다 하나씩 존재하며 지금 수행중인 명령어의 위치가 어디 있는지를 가리키고 스레드는 다음 명령을 실행하기 위해 PC레지스터의 주소를 참조합니다.
- 다른 스레드로 전환될 때 이전 스레드의 PC레지스터 값이 로드되어 실행됩니다.
- native영역의 메서드(다른 언어로 작성된 메서드)는 PC레지스터에서 값을 저장하지 않고 undefined상태로 남아 있으며 native Method 영억에서 별도 처리합니다.
네이티브 메서드 스택(Native Method Stack)
네이티브 메서드 스택은 자바 바이트 코드가 아닌 기계어나 다른 언어로 작성된 코드를 실행하기 위한 공간입니다. 일반 스택과 마찬가지로 스레드마다 독립적인 영역을 가지고 있습니다.
- 자바 바이트 코드로 된 메서드를 실행할 때는 JVM스택에 저장되지만 자바가 아닌 다른 언어로 작성된 코드의 경우 네이티브 메서드 스택에 쌓이게 됩니다.
- 스택영역이 스택 프레임을 생성하는 것처럼 네이티브 메서드가 호출되면 스택 프레임이 생성되고 메서드가 종료되면 없어집니다.
- 네이티브 메서드의 호출이 끝나면 자바 스택으로 돌아와서 작업을 계속하므로 네이티브 코드 함수 호출을 자바 코드에서도 할 수 있고 데이터를 전달할 수도 있습니다.
◆ 참고한 내용
- https://inpa.tistory.com/entry/JAVA-%E2%98%95-JVM-%EB%82%B4%EB%B6%80-%EA%B5%AC%EC%A1%B0-%EB%A9%94%EB%AA%A8%EB%A6%AC-%EC%98%81%EC%97%AD-%EC%8B%AC%ED%99%94%ED%8E%B8
- https://coding-factory.tistory.com/828
- https://www.youtube.com/watch?v=-p5vM1PSOVs
- https://www.youtube.com/watch?v=zta7kVTVkuk
'프로그래밍 > JAVA' 카테고리의 다른 글
[JAVA] String, StringBuffer, StringBuilder의 차이 (2) | 2024.07.23 |
---|