안녕하세요. 개발자 Jindory입니다.
이번 글에서는 JVM 메모리 구조에 대해서 알아보고자 합니다.
# 글 작성 이유
JVM의 구조는 어떻게 되어 있으며, Java에서 작성한 소스코드가 어떤 영역에서 데이터 및 메모리가 관리되고 실행되는지 이해하기 위해 작성한 글입니다.
JAVA 소스의 실행 구조
프로그램이 실행되기 위해서는 windows나 mac과 같은 운영체제(OS)가 제어하고 있는 시스템의 리소스의 일부인 메모리(RAM : 주 기억장치)를 제어할 수 있어야 합니다. JAVA 이전에 C 같은 대부분의 언어로 만들어진 프로그램은 이러한 이유 때문에 OS에 종속되어 실행되게 되어 있었습니다.
JAVA 프로그램은 JVM(Java Virtual Machine)이라는 프로그램만 있으면 실행이 가능한데, JVM이 OS에게 메모리 사용 권한을 할당 받고 JVM 자바 프로그램을 호출하여 실행하게 됩니다.
OS에서는 독립되었지만 JVM이라는 프로그램에는 종속적이게 되었습니다.(JVM을 실행 시키고 다시 JVM이 프로그램을 실행시키는 방식이다 보니 OS에 직접 제어를 받는 방식보다 속도면에서 느리다는 단점이 있습니다.)
JVM이란?
JVM이란 Java Virtual Machine의 약자로 자바 가상 머신이라고 부릅니다. JAVA와 OS 사이에서 중계자 역할을 하며 JAVA가 OS에 구애받지 않고 프로그램을 실행 할 수 있도록 도와줍니다. JVM은 다음과 같은 역할을 합니다.
- Garbage Collection를 사용하여 메모리 관리도 자동으로 수행하여 사용하지 않는 메모리에 대해서도 자동으로 관리해 줍니다.
- 메모리를 할당합니다.
- bytecode를 interpreter 형태로 OS에 맞추어 번역 및 실행합니다.
- 번역, 실행 시 최적화를 수행합니다.
JVM의 메모리 구조
JVM 구조는 크게 Garbage Collector, Execution Engine, Class Loader, Runtime Data Access 등 4가지로 나눌 수 있습니다.
Java compiler가 Java source 코드를 byte형태인 클래스 파일로 변환하면 Class 파일을 Class Loader가 읽어 들이면서 JVM이 수행됩니다.
- Class Loader
JVM 내로 클래스 파일을 로드하고, 클래스 파일을 사용하기 위해 검증하고, 기본 값으로 초기화하는 과정을 거칩니다. 또한 static 필드의 값들을 정의한 값으로 초기화하는 과정도 수행합니다.
해당 영역에 클래스정보(멤버변수의 이름), 변수정보(데이터 타입,접근제어자 정보), 메소드정보(메소드 이름, 리턴타입, 파라미터, 접근저어자 정보), static변수, final class 변수, Constant poo(상수풀 : 문자상수,타입,필드 객체참조가 저장됨) - Execution Engine
클래스 로더를 JVM내의 Runtime Data Area에 배치된 바이트 코드들을 명령어 단위로 읽어서 실행합니다. 최초 JVM이 나왔을 당시에는 인터프리터 방식이었기 때문에 속도가 느리다는 단점이 있었지만 JIT 컴파일러 방식을 통해 이 점을 보완했습니다. JIT는 바이트 코드를 어셈블러 같은 네이티브 코드로 바꿈으로써 실행이 빠르지만 역시 변환하는데 비용이 발생하였습니다. 이 같은 이유로 JVM은 모든 코드를 JIT 컴파일러 방식으로 실행하지 않고, 인터프리터 방식을 사용하다가 일정 기준이 넘어가면 JIT 컴파일러 방식으로 실행합니다. - Garbage Collector
힙 메모리 영역에 생성된 객체들 중에서 참조되지 않은 객체들을 탐색 후 제거하는 역할을 합니다.
GC가 이뤄지는 과정은 여기를 참고하면 쉽게 이해 할 수 있습니다. - Runtime Data Area
JVM 메모리 영역으로 JAVA Application을 실행할 때 사용되는 데이터와 명령어를 저장하기 위해 할당받은 메모리 공간입니다. 이 영역은 크게 Method Area, Heal Area, Stack Area, PC Register, Native Method Stack으로 나눌 수 있습니다.
- Method Area
- Method 영역은 클래스, 인터페이스, 메서드, 필드, static 변수 등의 바이트 코드를 보관합니다.
- 이 영역은 JVM이 종료될 때 까지 유지됩니다.
- 모든 Thread가 공유하는 메모리 영역입니다.
- Heap Area
- New 키워드로 생성된 객체와 인스턴스 변수 및 배열이 생성되는 영역입니다.
- 또한 메소드 영역에 로드된 클래스만 생성이 가능하고 Garbage Collector가 참조되지 않는 메모리를 확인하고 제거하는 영역입니다.
- 모든 Thread가 공유하는 메모리 영역입니다.
- Stack Area
- 각각의 스레드에 대한 별도의 런타임 스택 프레임이 생성하며 각 메소드 호출마다 새로운 스택 프레임이 생성되고, 메소드가 종료되면 해당 스택 프레임은 소멸됩니다.
- 그리고 메소드 안에서 사용되는 값들을 저장하고, 호출된 메소드의 매개변수, 지역변수, 리턴 값 및 연산시 일어나는 값들을 임시로 저장합니다.
- 메소드 호출의 LIFO(Last In, First Out) 순서를 유지하며, 이는 재귀 호출이나 중첩된 메소드 호출을 가능하게 합니다.
- PC Register
- 각 JVM Thread는 자체 PC Register를 가지며, 이는 현재 실행중인 명령의 주소를 저장합니다.
- Thread가 실행하는 메소드가 네이티브 메소드(C,C++,어셈블리 등의 언어로 작성된 메소드)가 아닌 경우, PC 레지스터는 현재 실행중인 JVM 명령의 주소를 포함합니다.(네이티브 메서드인 경우 PC Register의 값에 정의되지 않습니다)
- 따라서 PC Register는 JVM이 어떤 명령을 다음에 실행할지 결정하는 데 필요한 정보를 제공합니다. 각 Thread별로 생성됩니다.
- Native Method Stack
- Java 이외의 다른 프로그래밍 언어로 작성된 메소드를 관리하는데 사용합니다.
- Java 프로그램을 실행하기 위한 JVM에 Java가 아닌 다른 프로그래밍 언어로 작성된 Method를 관리하는 부분이 필요한 이유는 뭘까요?Java 플랫폼은 독립적인 언어이지만, 때때로 운영체제 또는 하드웨어에 특정한 기능이 필요하거나, 성능을 향상 시키기 위해 저수준 언어로 작성된 코드를 사용해야 할 때가 있습니다. 이런 경우에 Java가 직접 접근할 수 없는 시스템 특정 함수난 API를 사용해야합니다. 이러한 함수는 보통 C,C++ 등의 언어로 작성되며, 이를 'Native Method'라고 합니다.
- Native Method Stack은 이러한 Native Method의 실행을 추적하는 데 사용됩니다. Thread가 Native Method를 호출하면, JVM의 구조와 보안 제한을 벗어나게 되며, Native Method는 JVM의 런타임 데이터 영역에 접근 할 수 있게 됩니다. 이렇게 함으로써 JVM은 Java 이외의 다른 프로그래밍 언어로 작성된 코드와 상호 작용할 수 있게 됩니다.
- Method Area
이렇게 JVM의 메모리 구조에 대해서 알아봤습니다.
혹시라도 정정할 내용이나 추가적으로 필요하신 정보가 있다면 댓글 남겨주시면 감사하겠습니다.
오늘도 Jindory 블로그에 방문해주셔서 감사합니다.
[ 참조 ]
https://steady-coding.tistory.com/305
https://steady-developer-hyemin.tistory.com/26
https://www.oracle.com/webfolder/technetwork/tutorials/obe/java/gc01/index.html
'개발 > Java' 카테고리의 다른 글
[Java] Mutable Object와 Immutable Object (2) | 2023.12.15 |
---|---|
[JAVA] static에 관하여 (0) | 2023.12.13 |
[Java] 컴퓨터가 실수를 표현하는 방법과 실수 연산시 주의사항(IEEE 754) (0) | 2023.12.12 |
[Java] LocalDate,LocalTime,LocalDateTime 활용하기 (0) | 2022.11.02 |
[Java] Windows 환경에서 jdk버전 2개 이상 관리 (0) | 2022.09.26 |