3. Runtime Data Areas
3.1 Method Area (Metaspace - Java 8+)
저장 내용:
- 클래스 메타데이터 (클래스명, 부모 클래스, 인터페이스)
- static 변수
- 메서드 정보 (메서드명, 반환 타입, 파라미터)
- 상수 풀 (Constant Pool)
public class MethodAreaExample {
// Method Area에 저장
static int staticVar = 100;
static final String CONSTANT = "Hello";
public void method() {
// 메서드 정보도 Method Area에 저장
}
}
Java 7 vs Java 8 변화:
Java 7 (PermGen):
┌─────────────┐
│ Heap │
├─────────────┤
│ PermGen │ ← 고정 크기 (-XX:PermSize, -XX:MaxPermSize)
│ - Classes │ OutOfMemoryError: PermGen space
│ - Strings │
└─────────────┘
Java 8+ (Metaspace):
┌─────────────┐
│ Heap │ ← String Pool 이동
└─────────────┘
┌─────────────┐
│ Metaspace │ ← Native Memory (동적 크기)
│ - Classes │ -XX:MetaspaceSize, -XX:MaxMetaspaceSize
└─────────────┘
JVM 옵션:
# Java 8+
-XX:MetaspaceSize=128m # 초기 크기
-XX:MaxMetaspaceSize=512m # 최대 크기
# Metaspace 모니터링
jstat -gc <pid>
3.2 Heap (힙 영역)
구조 (Generational Heap):
┌─────────────────────────────────────────────────────────┐
│ Heap │
│ │
│ ┌──────────────────────────┐ ┌──────────────────────┐ │
│ │ Young Generation │ │ Old Generation │ │
│ │ │ │ │ │
│ │ ┌──────┐ ┌───────────┐ │ │ │ │
│ │ │ Eden │ │ Survivor │ │ │ Tenured (장기) │ │
│ │ │ │ │ S0 │ S1 │ │ │ │ │
│ │ └──────┘ └───────────┘ │ │ │ │
│ │ (새 객체) (임시 보관) │ │ (오래된 객체) │ │
│ └──────────────────────────┘ └──────────────────────┘ │
│ ▲ ▲ │
│ │ Minor GC │ Major GC │
└───────────┼──────────────────────────────┼───────────────┘
│ │
빠르고 빈번 느리고 드물게
객체 생성 흐름:
public class HeapAllocationExample {
public static void main(String[] args) {
// 1. Eden 영역에 객체 생성
User user1 = new User("Alice");
// 2. Eden이 가득 차면 Minor GC 발생
for (int i = 0; i < 1000000; i++) {
User temp = new User("User" + i);
// Eden → Survivor 0 이동
}
// 3. 여러 번 Minor GC 생존 → Old Generation 이동
// user1 참조가 계속 유지 → Old로 Promotion
}
}
Age-based Promotion:
Eden → S0 → S1 → S0 → ... (15번 반복) → Old Generation
객체의 Age:
- Minor GC 때마다 Age + 1
- -XX:MaxTenuringThreshold=15 (기본값)
- Age >= 15 → Old Generation 이동
JVM 힙 옵션:
# Heap 크기 설정
-Xms2g # 초기 Heap 크기
-Xmx4g # 최대 Heap 크기
# Young/Old Generation 비율
-XX:NewRatio=2 # Old:Young = 2:1
-XX:SurvivorRatio=8 # Eden:Survivor = 8:1:1
# 예시: -Xmx4g -XX:NewRatio=2
# Heap 4GB → Young 1.33GB, Old 2.67GB
# Young 1.33GB → Eden 1.07GB, S0 133MB, S1 133MB
3.3 JVM Stack (스택 영역)
구조:
Thread 1 Stack Thread 2 Stack
┌─────────────┐ ┌─────────────┐
│ Frame 3 │ │ Frame 2 │
├─────────────┤ ├─────────────┤
│ Frame 2 │ │ Frame 1 │
├─────────────┤ └─────────────┘
│ Frame 1 │
└─────────────┘
각 Frame 구조:
┌─────────────────────────┐
│ Local Variables │ ← 지역 변수, 매개변수
├─────────────────────────┤
│ Operand Stack │ ← 연산 중간 결과
├─────────────────────────┤
│ Frame Data │ ← 메서드 정보, 리턴 주소
└─────────────────────────┘
예제:
public class StackExample {
public static void main(String[] args) { // Frame 1
int x = 10; // Local Variable
int result = add(x, 20); // Frame 2 생성
System.out.println(result);
} // Frame 1 pop
public static int add(int a, int b) { // Frame 2
int sum = a + b; // Local Variable
return sum; // Frame 2 pop, 결과를 Frame 1의 Operand Stack으로
}
}
Stack 메모리 흐름:
1. main() 호출 → Frame 1 push
Local Variables: args, x=10, result=?
2. add(10, 20) 호출 → Frame 2 push
Local Variables: a=10, b=20, sum=30
Operand Stack: 30 (리턴값)
3. add() 종료 → Frame 2 pop
result = 30 (Operand Stack에서 가져옴)
4. main() 종료 → Frame 1 pop
StackOverflowError:
public class StackOverflowExample {
public static void main(String[] args) {
recursiveMethod(0);
}
public static void recursiveMethod(int depth) {
System.out.println("Depth: " + depth);
recursiveMethod(depth + 1); // 무한 재귀
// StackOverflowError 발생!
}
}
// JVM 옵션으로 Stack 크기 조정
// -Xss1m (기본값: 1MB)
3.4 PC Register (Program Counter)
역할:
- 현재 실행 중인 JVM 명령어 주소 저장
- 각 스레드마다 독립적으로 존재
public class PCRegisterExample {
public static void main(String[] args) {
int a = 10; // PC: 라인 3
int b = 20; // PC: 라인 4
int c = a + b; // PC: 라인 5
// PC Register는 다음에 실행할 명령어 주소를 가리킴
}
}
Bytecode로 보는 PC:
Bytecode:
0: bipush 10 ← PC = 0
2: istore_1 ← PC = 2
3: bipush 20 ← PC = 3
5: istore_2 ← PC = 5
6: iload_1 ← PC = 6
7: iload_2 ← PC = 7
8: iadd ← PC = 8
9: istore_3 ← PC = 9
3.5 Native Method Stack
JNI (Java Native Interface) 호출 시 사용:
public class NativeMethodExample {
// Native 메서드 선언
public native void nativeMethod();
static {
// C/C++ 라이브러리 로드
System.loadLibrary("native-lib");
}
public static void main(String[] args) {
new NativeMethodExample().nativeMethod();
// Native Method Stack 사용
}
}
📚 다음 편: 준비 중입니다.
💬 댓글