-
[Java] JVM의 메모리 영역 (JVM Memory Structure)Java 2024. 10. 29. 22:57
JVM(Java Virtual Machine)의 메모리 영역은 Java 애플리케이션이 실행될 때 사용하는 메모리 공간을 여러 영역으로 나눈 것이다. 크게 5가지 영역으로 구분되며, 각 영역은 고유한 역할을 가지고 있다. 최적화가 요구되는 경우 GC 튜닝, 힙 영역 설정, 쓰레드 스택 크기 조정 등이 이루어질 수 있다.
JVM Memory Structure
1. Method Area
클래스에 대한 구조적 정보를 저장하는 곳이다. 메서드 영역은 JVM에 의해 한 번 로드되면 프로그램 종료 시까지 유지된다. Java 8 이전까지는 PermGen(Permanent Generation) 영역으로 관리되었으나, Java 8 이후부터는 PermGen이 사라지고 Metaspace라는 별도 영역으로 관리되고 있다. Metaspace는 힙 영역 밖의 네이티브 메모리를 사용하여 자동으로 크기가 조정되어 OutOfMememoryError가 발생할 가능성이 감소했다.
저장되는 데이터- 클래스 정보 : 클래스명, 부모 클래스명, 메서드 정보, 필드 정보 등 클래스의 구조와 관련된 데이터가 저장된다.
- 상수 풀 (Constant Pool) : 클래스나 인터페이스가 사용하는 상수들이 저장되는 곳이다. 메서드 호출에 필요한 *심볼릭 참조, 상수 값 등이 포함된다. 이 영역은 런타임 상수 풀이라 불리기도 하며, *컴파일 타임 상수 풀과는 다르다.
- 메서드 코드 : 클래스의 메서드 바이트 코드가 저장되며, 실제로 런타임에 메서드가 호출될 때 참조되는 코드가 이곳에 저장된다.
- JIT 컴파일된 코드 : *JIT(Just-In-Time) 컴파일러에 의해 *네이티브 코드로 변환된 메서드 코드가 캐시된다.
심볼릭 참조 (Symbolic Reference) : 프로그램이 실행되는 동안 참조 대상을 직접 메모리 주소로 지정하지 않고, 심볼(이름)로 접근하는 방식이다. 클래스, 메서드, 필드 등을 심볼릭 참조로 런타임에 필요한 위치를 찾아간다. JVM은 클래스 로딩 시 심볼릭 참조를 실제 메모리 주소로 변환하는데, 이를 리졸빙(Resolving)이라고 한다.
컴파일 타임 상수 풀 : 자바 컴파일러가 컴파일 시에 클래스 파일에 저장하는 상수 값들의 모음이다. 이곳에는 기본 데이터 타입의 상수 값과 문자열 리터럴이 포함된다. 이 풀은 클래스가 로드될 때 메서드 영역에 로드되어 런타임 상수 풀의 일부로 사용된다.
JIT 컴파일러 : JVM이 자바 바이트 코드를 실행할 때, 반복적으로 실행되는 메서드나 코드 블록을 네이티브 머신 코드로 변환하여 성능을 최적화하는 컴포넌트이다.
네이티브 코드 : 자바 바이트 코드를 운영체제와 CPU에서 직접 실행할 수 있도록 변환된 기계어이다. 네이티브 코드는 JVM을 거치지 않고 실행되므로 더 빠르게 수행된다.
2. Heap
JVM의 가장 큰 메모리 영역으로, 객체와 배열을 저장하는 공간이다. GC(Garbage Collector)가 주로 이 영역을 관리한다. 메모리 누수나 객체의 과도한 할당으로 OutOfMemoryError가 발생할 수 있다.
힙의 세 가지 영역- Eden 영역 : 새롭게 생성된 객체가 우선적으로 할당되는 영역이다. 객체가 생성되면 먼저 이곳에 저장되며, 이 영역이 가득차면 *Minor GC가 발생하여 더 이상 참조되지 않는 객체가 제거된다.
- Survivor 영역 : Eden 영역에서 살아남은 객체가 이동되는 두 개의 영역(Survivor 0, Survivor 1)으로 구성된다. GC에서 살아남은 객체는 Eden에서 Survivor 영역으로 이동하며, 이곳에서도 계속 살아남는 객체는 Tenured 영역으로 이동한다.
- Tenured(Old) : 비교적 수명이 긴 객체가 저장되는 공간이다. Minor GC를 여러 번 거친 객체들이 이 영역에 저장되며, 주로 큰 객체나 긴 수명을 가진 객체들이 이에 해당된다. 이 영역이 가득차게 되면 *Major GC 또는 Full GC가 발생한다.
Minor GC : Eden 영역에서 주로 발생하며 새로 생성된 객체들이 주로 이 영역에 할당되기 때문에 비교적 빈번하게 발생한다. Minor GC가 발생할 때 Eden 영역에서 더 이상 참조되지 않는 객체가 제거된다. 짧은 시간 내에 이루어지며 애플리케이션 성능에 큰 영향을 주지 않는다.
Major GC : Old 영역에서 발생하는 가비지 컬렉션으로, Minor GC를 여러 번 거쳐 생존한 객체들이 Old 영역에 쌓여 가득차면 발생한다. Major GC는 Old 영역 전체를 검사하여 불필요한 객체를 제거하기 때문에 실행 시간이 길어 성능에 영향을 미칠 수 있다. 이때 *STW 현상이 발생하여 애플리케이션의 모든 쓰레드가 일시 중단될 수 있다.
STW (Stop-The-World) : JVM이 가비지 컬렉션 또는 특정 작업을 수행할 때 애플리케이션의 모든 쓰레드를 일시 중단하는 현상이다. GC가 메모리를 안전하게 정리하기 위해 애플리케이션의 실행을 멈추고, 작업이 완료되면 다시 쓰레드를 재개한다. STW는 애플리케이션 응답 속도에 영향을 줄 수 있기 때문에 GC 튜닝의 주요 고려 사항이다.
3. Stack
스택 영역은 각 쓰레드별로 생성되는 메모리 공간으로, 메서드 호출 시 호출된 메서드의 프레임이 저장된다. 각 프레임은 해당 메서드의 지역 변수, 매개 변수, 연산 중간 결과 등을 포함하며, 메서드가 종료되면 제거된다. 스택 영역은 정해진 크기로 제한되어 있지만 메모리 할당과 해제가 프레임 단위로 이루어져 속도가 매우 빠르다. 메서드 호출이 깊어지면 StackOverflowError가 발생할 수 있다.
대표적인 구성 요소- 지역 변수
- 연산자 스택 (Operand Stack) : 메서드 내에서 수행되는 연산 과정 중 피연산자와 결과를 임시로 저장하는 공간이다.
- 프레임 데이터 : 메서드 호출 시 필요한 정보, 예를 들어 메서드에서 참조하는 상수 풀 참조 등이 포함된다.
4. PC (Program Counter) Register
PC 레지스터는 각 쓰레드가 실행할 명령의 주소를 저장하는 작은 메모리 공간이다. 각 쓰레드가 자신만의 PC 레지스터를 가지는 쓰레드별 독립적인 공간이다. 네이티브 메서드가 호출되는 경우에는 PC 레지스터가 특정 명령 주소를 참조하지 않는다. 즉, 네이티브 메서드를 실행하는 동안 PC 레지스터는 자바 바이트코드의 위치가 아닌 네이티브 코드 흐름을 따르게 되어 명령어 주소 참조는 의미가 없거나 무시되는 상태인 것이다.
5. Native Method Stack
네이티브 메서드 스택은 JVM이 Java가 아닌 C, C++ 같은 네이티브 메서드를 호출할 때 사용하는 영역이다. 이 스택은 *JNI를 통해 네이티브 코드를 호출할 때 생성된다. 네이티브 메서드 스택의 크기는 운영체제와 JVM 설정에 따라 다르며, 일반적으로 자바 스택과 비슷한 방식으로 관리된다. 네이티브 메서드 실행 중 문제가 발생하면 OutOfMemoryError가 발생할 수 있다.
JNI (Java Native Interface) : 자바 프로그램이 C, C++ 같은 네이티브 언어로 작성된 코드와 상호작용할 수 있게 해 주는 인터페이스이다. 이를 통해 자바에서 운영체제 기능을 직접 호출하거나 고성능 네이티브 라이브러리를 사용할 수 있다. JNI는 자바와 네이티브 코드 간 데이터 교환과 메서드 호출을 가능하게 하여 자바의 기능 확장에 활용된다.
코드로 복습!
public class Example { private static int counter = 0; // Method Area에 저장 public void increment() { int temp = counter; // Stack에 저장 temp++; // Stack 연산자 스택 사용 counter = temp; // Heap에 저장된 인스턴스 접근 } }
'Java' 카테고리의 다른 글
[Java] Dump File에 대해서 (0) 2024.10.31 [Java] Native Resource와 native 예약어 (1) 2024.10.30 [Java] Comparable과 Comparator (0) 2024.10.25 [Java] 자바 직렬화에 대해서 (Serialization) (2) 2024.10.23 [Java] parseInt() 메서드 직접 구현하기 (0) 2024.09.04