CS

JVM 메모리 구조

🤖 Play with Android 🤖 2022. 1. 19. 22:00
728x90

 

Java는 .java 파일을 javac 컴파일러를 통해 .class 바이트코드로 컴파일한 후, 클래스 로더에 의해 .class 파일을 JVM 위의 Runtime Data Area에 올려서 실행시키기에 OS 독립적으로 개발할 수 있다.

 

 

📌  JVM의 동작 방식 및 순서

1. 프로그램 실행 시 JVM은 OS로부터 메모리를 할당 받는다.
2. 자바 컴파일러(javac)가 자바 소스코드(.java)를 자바 바이트코드(.class)로 컴파일한다.
3. Class Loader를 통해 JVM Runtime Data Area로 로딩된다.
4. 로더에 의하여 로딩 된 .class들은 Execution Engine을 통해 Interpret(해석)된다.
5. 해석된 바이트 코드는 Runtime Data Area의 각 영역에 배치되어 수행된다.
이 과정에서 Execution Engine에 의해 GC의 작동과 쓰레드 동기화가 이루어진다.

 

 

 

이와 같이 Java 애플리케이션을 실행하는 경우, JVM 메모리에는 여러 가지 데이터가 로드되고 해제된다.

JVM의 메모리는 어떤 객체를 저장하고 어떤 용도로 사용되는지에 따라 여러 영역으로 나뉘어 있다.

 

 

 

 

📌  JVM 메모리 각 영역별 역할 및 특징

JVM의 메모리 영역은 Runtime Data Area라고도 불리며, 크게 Method Area, Heap Area, Stack Area, PC Register, Native Method Stack로 나눌 수 있다.

JVM 메모리 구조

 

 

📔  Method Area

JVM에서 읽은 클래스와 인터페이스의 정보가 저장되는 영역이며, 클래스 생성자 및 메서드의 코드(바이트 코드) 등 이 저장된다. 클래스의 인스턴스가 생성된 후, 메서드가 실행되는 순간 클래스의 정보가 Method Area에 저장된다.

Method Area는 모든 Thread에 의해 공유되는 영역이며, JVM이 시작될 때 생성된다.

 

또한 Method Area 내부에는 Runtime Constant Pool이라는 영역이 존재하는데,

  • 각 클래스, 인터페이스 상수, 메서드 필드와 모든 레퍼런스가 담겨있다.
  • 런타임 상수 풀의 역할은 이미 있는 메서드나 필드의 참조를 통해 중복을 막는 역할을 한다.

 

 

📔  Heap

객체(인스턴스)와 배열 등 을 저장되는 영역이다. Heap 영역에 저장된 객체(인스턴스)나 배열은 다른 객체에서 참조될 수 있다.

GC(Garbage Collector)가 발생하는 영역이며, 참조(레퍼런스)가 없는 객체들은 GC과정을 통해 메모리에서 제거된다. Heap 영역 또한 내부적으로 여러 영역으로 나뉘어 있으며, 이는 객체의 lifecycle 및 GC와 연관되어 있다.

Java(Kotlin)는 C언어와 달리 동적 할당된 인스턴스들을 직접 소멸시키지 않아도 GC에 의해 자동으로 관리되기 때문에 메모리 관리에 덜 신경 써도 된다는 특징이 있다.

JVM Heap 구조

 

Heap 영역은 크게 Permanent Generation 영역, New 영역, Old영역으로 나뉜다.
객체를 생성할 때 필요한 정보를 저장하는 공간인 Permanent 영역, 객체가 생성되는 New영역, 생성된 객체를 오랫동안 참조할 가능성이 있는 객체들을 저장하는 공간이 Old 영역이다.

Heap 영역은 위에서 말했다시피 new 연산자 등으로 생성된 객체(인스턴스)와 배열 등을 저장되는 영역으로서 GC가 발생한다.

GC는 한정적인 메모리 자원을 효율적으로 사용하기 위해 더 이상 불필요한 리소스들을 메모리에서 제거하는 작업을 의미한다. 이때, 불필요한 리소스들을 추적/관리하기 위해 Heap의 각 영역들이 필요하다.

 

 

Eden 영역(Young 영역)

새로 생성된 대부분의 객체가 처음 위치하는 영역을 의미한다. 이곳에서 GC가 발생한 후, 살아남은 객체들은 Survivor 영역으로 이동하여 계속 쌓이게 된다.

 

 

Survivor1, Survivor2 영역

Eden 영역에서 GC가 발생하면 Survivor 영역에 객체가 계속 쌓이게 된다. 만약 쌓이던 Survivor1 영역이 가득 차게 되면, 여전히 생존한(즉 참조되고 있는) 객체를 나머지 Survivor 영역에 옮긴다. 그런 뒤 Survivor1은 비우고 이러한 과정을 반복하게 된다. 

이러한 메커니즘 때문에 Survivor 영역 중 하나의 영역은 항상 비어 있는 상태이다. 

※ Minor GC : Eden 영역, 혹은 Survivor 영역에서 발생하는 GC를 Minor GC라고 한다.

 

 

Old 영역

Young영역에서 저장되어 있던 객체 중 오래 살아남은 객체들이 저장되는 영역이다. 

보통 Old 영역은 Young 영역보다 크게 할당하며, 이러한 이유로 Old 영역의 GC는 Young 영역보다 적게 발생한다.

※ Major GC : Old 영역, Permanent 영역에서 발생하는 GC를 Major GC(Full GC)라고 한다.

 

 

Permanent 영역

이 영역은 java8부터 heap space는 아니라고 한다. native area로 이동되었다고 한다.

용어 또한 meta space로 바뀌었다.  

 

 

 

📔  JVM Language Stacks

메서드 호출 시 수행 중인메서드 데이터(지역변수, 지역객체 레퍼런스, 메소드 파라미터, 메소드 리턴 값 등)를 저장하기 위한 영역이다.

Stack 영역은 Thread별로 각각 독립적으로 생성된다.

Stack 영역에는 메서드 진입시마다 메소드 데이터(지역변수, 지역객체 레퍼런스, 메소드 파라미터, 메소드 리턴 값 등)를 포함하는 Stack Frame 이 생성되어 Push 되며, 메서드 생성이 완료되면 Stack Frame은 pop 되어 사라진다.

printStackTrace()를 통해 Stack Trace로 각 스택 프레임을 출력하여 확인할 수 있다.

 

 

📔  PC Register

 Thread 마다 할당되는 영역이며, Thread가 시작될 때 생성된다. 또한 현재 수행 중인 JVM의 명령어 주소(바이트 코드 명령)를 가지고 있다.

Java 코드가 컴파일 과정을 통해 변환된 결과물(바이트코드)은 여러 바이트코드 명령들이 나열된 형태가 되는데, JVM은 이러한 명령을 하나씩 실행하며, Java 애플리케이션을 실행해 나간다. 

JVM이 스택 기반의 PC register를 사용하는 목적은 JVM이 플랫폼에 독립적으로 동작하기 위함이다.

 

 

 

📔  Native Method Area

Native method란 자바 외의 다른 언어(C, C++ 등)에서 제공되는 메서드들을 의미한다. 이 영역은 Native method의 변수를 저장하는 공간이다. 이를 수행하기 위해서 JVM은 JNI(Java Native Interface)라는 표준규약을 통해서 다른 언어에서 제공하는 메서드를 사용한다.

 

 

 


 

 

 

 

위의 까지의 내용을 그림으로 표현하면 다음과 같다.

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

Reference:

https://kils-log-of-develop.tistory.com/311

https://developer111.tistory.com/33

https://smjeon.dev/etc/jvm-gc/

https://jeong-pro.tistory.com/148

https://mirinae312.github.io/develop/2018/06/04/jvm_memory.html

https://velog.io/@agugu95/%EC%9E%90%EB%B0%94%EC%99%80-JVM-%EA%B7%B8%EB%A6%AC%EA%B3%A0-%EB%A9%94%EB%AA%A8%EB%A6%AC-%EA%B5%AC%EC%A1%B0