JVM Memory Model

개발자 춘식이·2023년 7월 12일
0
post-thumbnail

자바 메모리 모델을 알기 위해서는 자바의 JVM 아키텍처에 대해서 먼저 알 필요가 있습니다. 모든 프로그래밍 언어는 그 언어를 실행하기 위한 컴포넌트, 라이브러리, API 등이 있어야 합니다. 자바는 JREJDK가 이에 해당합니다.

  • JRE(Java Runtime Environment): JRE는 자바 애플리케이션이 구동되기 위한 최소한의 환경입니다. JVM과 개발 도구들이 포함되어 있습니다.
    • JVM(Java Virtual Machine): OS에 독립적으로 구동될 수 있도록 JVM 위에 애플리케이션이 돌아갑니다.
  • JDK(Java Development Kit): 개발자는 개발하기 위해 JDK를 설치해야 합니다. JDK는 완전한 개발 환경을 만들어 주며, JRE와 개발 도구를 포함합니다.

JVM 아키텍처

JVM Architecture

1. Class Loader Subsystem

JVM은 RAM 안에서 상주합니다. 자바를 실행하게 되면, Class Loader Subsystem이 실행되면서 클래스 파일(.class)들을 RAM으로 가져오는데, 이를 동적 클래스 로딩이라고 합니다. Class Loader Subsystem을 통해 런타임 시 처음으로 로드될 때 동적으로 클래스 파일을 Loading, Linking 및 Initialization 합니다.

  • Loading: 특정 이름을 가진 클래스 또는 인터페이스 바이너리 데이터를 찾고 JVM method area에 저장합니다.
  • Linking: 클래스 또는 인터페이스를 가져와 실행될 수 있도록 런타입 상태의 JVM에 결합해 주는 프로세스입니다.
  • Initialization: 컴파일러에 의해 제공되는 클래스 및 인터페이스 초기화 메서드 <clinit> 실행을 해 초기화합니다.

2. Runtime Data Area

Runtime Data Area

Runtime Data AreaJVM이 OS에서 실행될 때 할당받는 메모리 영역입니다.

Class Loader Subsystem은 클래스 파일을 읽는 것 외에도 바이너리 데이터를 생성하고 각 클래스에 대한 정보를 Method Area에 적재합니다. 모든 클래스 파일이 로드가 완료되면, heap 메모리에 클래스 파일을 나타내는 하나의 클래스 객체를 만듭니다. 이 클래스 객체는 나중에 코드에서 클래스 정보(클래스명, 부모명, 메서드, 변수정보, 정적 변수 등)을 읽는데 사용됩니다.

1) Method Area(=Static Area, Class Area)

Method Area는 JVM이 시작될 때 생성되며, JVM 한 개에 하나의 Method Area만 생깁니다. 모든 JVM thread들은 같은 Method Area를 공유합니다. Method Area는 논리적으로 Heap에 포함되며, Java 7 이전에는 Heap의 PermGen이라는 영역에 속했지만, Java 8 이후로는 Metaspace라는 OS가 관리하는 영역으로 옮겨졌습니다. Method Area는 static 변수, 런타임 상수 풀, 필드 데이터, 메소드 데이터, 클래스로더 레퍼런스 등 클래스 레벨의 데이터를 저장합니다.

2) Heap

Heap 또한 JVM 당 하나의 Heap Area가 생기며, 모든 JVM thread들은 같은 Heap을 공유합니다. new 연산자로 생성된 모든 객체와 관련된 인스턴스 변수와 배열, 문자열에 대한 정보를 가진 String Pool 등이 Heap 영역에 저장되며, GC의 대상이 됩니다. Method Area와 Heap은 멀티 스레드에서 공유되는 메모리로, Method Area와 Heap Area에 저장되는 데이터들은 thread safe 하지 않습니다.

Java7HotSpotJVM Java8HotSpotJVM

Java 7의 Permanent 영역은 보통 Class의 메타 정보나 method의 메타 정보, Static 변수와 상수 정보들이 저장되는 공간으로 흔히 메타데이터 저장 영역이라고도 합니다. Java 8부터는 Native 영역으로 이동하여 Metaspace 영역으로 변경되었습니다. 기존 permanent 영역에 존재하던 Static Object 변수, 상수는 Heap 영역으로 이동되었습니다.
Heap 영역은 JVM에 의해 관리되는 영역이며, Native 영역은 OS 레벨에서 관리하는 영역으로 구분됩니다. Metaspace가 Native 메모리를 이용함으로써 개발자는 영역 확보의 상한을 크게 신경 쓸 필요가 없어져서 제거되었습니다.

3) Stack

StackFrame

Stack은 공유 자원이 아니므로 thread safe하고 각 thread가 시작되면, 메서드 호출을 저장하기 위한 별도의 런타임 스택이 각각 생성됩니다. 모든 메소드를 호출할 때마다 하나의 Frame이 생성되어 런타임 스택의 맨 위에 추가(push)되며 이러한 항목을 Stack Frame이라고 합니다.

각 stack frame은 실행 중인 메서드가 속한 클래스의 로컬 변수 배열, 피연산자 스택(메소드 내 연산을 위해 바이트 코드 명령문들이 있는 공간), 런타임 상수 풀에 대한 참조가 있습니다. 로컬 변수 배열과 피연산자 스택의 크기는 컴파일하는 동안 결정됩니다. 따라서 스택 프레임의 사이즈는 메소드에 의해 크기가 고정됩니다.

메소드가 정상적으로 실행 후 종료되거나 에러가 발생했을 때 해당 frame은 stack으로부터 제거(pop)됩니다. 만약 에러가 발생했다면, 각 줄의 stack trace(pringStackTrace() 메소드명의 유래가 여기서 나오네요.)를 한 스택 프레임에 나타냅니다.

stack의 사이즈는 동적이거나 고정될 수 있습니다. 만약 스레드가 큰 스택을 요구한다면 StackOverflowError가 발생하고, 만약 새로운 frame을 생성해야 하는데 메모리가 충분하지 않다면 OutOfMemoryError가 발생합니다.

4) PC Registers

각 JVM 스레드가 시작되면 PC Register는 현재 실행 중인 명령의 주소(Method area의 메모리 주소)를 기억하기 위해 PC(Program Counter) Register가 생성됩니다. 실행이 끝나면, PC register는 다음 명령의 주소로 업데이트됩니다.

5) Native Method Stack

Java thread에 대한 모든 상태를 준비한 후 JNI를 통해 호출되는 네이티브 메소드 정보(C/C++)를 저장하기 위해 별도의 기본 스택이 생성됩니다.

네이티브 스레드가 한 번 생성되고 초기화되면, 자바 스레드의 run() 메소드를 호출합니다. run() 메소드가 리턴될 때 uncaught exeption을 포착하면 네이티브 스레드는 JVM을 종료해야 하는지 여부를 확인합니다. 스레드가 종료되면 모든 네이티브 및 자바 스레드의 리소스들은 해제됩니다. 네이티브 스레드는 자바 스레드가 종료되면 회수됩니다. 따라서 운영 체제는 모든 스레드를 스케줄링할 수 있고 사용할 수 있는 CPU로 참조합니다.


3. Execution Engine

bytecode의 실제 실행은 Execution Engine에서 이루어집니다. Execution Engine은 Runtime data areas에 할당된 데이터를 읽어 bytecode의 명령을 한 줄씩 실행합니다.

1) Interpreter

Interpreter는 바이트코드를 해석하고 명령을 하나씩 실행합니다. 하나의 바이트코드 라인을 빠르게 해석할 수는 있지만, 그 결과를 실행하는 작업은 느립니다.

2) Just-In-Time(JIT) Compiler

JIT는 모든 바이트코드를 네이티브코드(machine code)로 컴파일하고, 반복되는 메소드 호출에 대해 네이티브 코드를 직접 제공하여 향상된 속도를 제공합니다. 네이티브 코드는 캐시에 저장되므로 컴파일된 코드를 더 빨리 실행할 수 있습니다.

3) Garbage Collector (GC)

객체가 더이상 참조되지 않으면 GC는 해당 객체를 제거하고 사용되지 않은 메모리를 회수합니다.


참고
Understanding JVM Architecture
JVM에 관하여 - Part 3, Run-Time Data Area
자바 메모리 구조(Runtime Data Area)
Java Heap (with GC)
JDK 8에서 Perm 영역은 왜 삭제됐을까

지속적으로 수정해나갈 예정입니다.
2023-07-13 v1.0

profile
춘식이를 너무 좋아하는 주니어 백엔드 개발자입니다.

0개의 댓글