JVM 메모리 구조 전체 그림
┌──────────────────────────────────────────────────────────┐
│ JVM Memory │
├──────────────────────────────────────────────────────────┤
│ │
│ ┌──────────────────────────────────────────────────┐ │
│ │ Method Area (Metaspace) │ │
│ │ │ │
│ │ • 클래스 메타데이터 │ │
│ │ • static 변수들 │ │
│ │ • static 메서드 코드 │ │
│ │ • 상수 풀 (Constant Pool) │ │
│ │ │ │
│ └──────────────────────────────────────────────────┘ │
│ ↕ │
│ ┌──────────────────────────────────────────────────┐ │
│ │ Heap │ │
│ │ │ │
│ │ • new로 생성한 모든 객체 │ │
│ │ • 인스턴스 변수들 │ │
│ │ • 배열 │ │
│ │ │ │
│ │ [객체1] [객체2] [객체3] ... │ │
│ │ │ │
│ └──────────────────────────────────────────────────┘ │
│ ↕ │
│ ┌──────────────────────────────────────────────────┐ │
│ │ Stack │ │
│ │ │ │
│ │ • 메서드 호출 정보 │ │
│ │ • 지역 변수 │ │
│ │ • 매개변수 │ │
│ │ • 리턴 값 │ │
│ │ │ │
│ └──────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────┘
실제 코드로 메모리 동작 이해
java
public class BankAccount {
// 인스턴스 변수 - Heap에 저장
private String accountHolder;
private double balance;
// static 변수 - Method Area에 저장
private static int totalAccounts = 0;
private static double interestRate = 0.02;
// 생성자
public BankAccount(String holder, double initial) {
this.accountHolder = holder;
this.balance = initial;
totalAccounts++;
}
// 인스턴스 메서드
public void deposit(double amount) {
balance += amount;
}
// static 메서드
public static void setInterestRate(double rate) {
interestRate = rate;
}
}
코드 실행 시 메모리 변화
Step 1: 프로그램 시작
Method Area:
┌────────────────────┐
│ BankAccount.class │
│ totalAccounts: 0 │
│ interestRate: 0.02 │
└────────────────────┘
Heap: (비어있음)
Stack:
┌────────────────────┐
│ main() 시작 │
└────────────────────┘
Step 2: 첫 번째 계좌 생성
java
BankAccount acc1 = new BankAccount("김철수", 1000000);
Method Area:
┌────────────────────┐
│ totalAccounts: 1 │ ← 증가!
│ interestRate: 0.02 │
└────────────────────┘
Heap:
┌────────────────────┐
│ acc1 객체 │
│ holder: "김철수" │
│ balance: 1000000 │
└────────────────────┘
Stack:
┌────────────────────┐
│ acc1 = @주소1 │ ← Heap 객체 가리킴
└────────────────────┘
Step 3: 두 번째 계좌 생성
java
BankAccount acc2 = new BankAccount("박영희", 2000000);
Method Area:
┌────────────────────┐
│ totalAccounts: 2 │ ← 또 증가!
│ interestRate: 0.02 │
└────────────────────┘
↑ 공유 ↑
Heap:
┌────────────────────┐ ┌────────────────────┐
│ acc1 객체 │ │ acc2 객체 │
│ holder: "김철수" │ │ holder: "박영희" │
│ balance: 1000000 │ │ balance: 2000000 │
└────────────────────┘ └────────────────────┘
Stack:
┌────────────────────┐
│ acc1 = @주소1 │
│ acc2 = @주소2 │
└────────────────────┘
Step 4: static 메서드 호출
java
BankAccount.setInterestRate(0.03); // 이자율 변경
Method Area:
┌────────────────────┐
│ totalAccounts: 2 │
│ interestRate: 0.03 │ ← 변경! (모든 계좌 영향)
└────────────────────┘
⚠️ static 메서드가 인스턴스 변수 접근 못하는 이유
java
public static void wrongMethod() {
System.out.println(balance); // 에러!
}
시각적 설명:
BankAccount.wrongMethod() 호출 시:
Method Area에서 실행
↓
"balance를 출력하라고?"
↓
Heap을 봐야 하는데...
↓
acc1의 balance? acc2의 balance?
↓
알 수 없음! → 컴파일 에러
메모리 영역별 특징
영역 | 저장되는 것 | 생성 시점 | 소멸 시점 |
Method Area | static 변수/메서드, 클래스 정보 | 클래스 로딩 시 | 프로그램 종료 |
Heap | 객체, 인스턴스 변수, 배열 | new 실행 시 | GC가 수거 |
Stack | 지역변수, 매개변수, 메서드 호출 | 메서드 호출 시 | 메서드 종료 |
핵심 정리
접근 규칙
인스턴스 메서드 → 모든 영역 접근 가능
static 메서드 → Method Area만 접근 가능
실용적 비유
Method Area = 회사 게시판 (모두가 봄)
Heap = 각자의 책상 (개인 소유)
Stack = 임시 메모장 (작업 중에만 사용)
'Java' 카테고리의 다른 글
Java | 생성자 체인(Constructor Chaining) (0) | 2025.10.11 |
---|---|
Java | Hiding (0) | 2025.10.04 |
Java | 정적 변수 vs 정적 메서드 (2) | 2025.10.04 |
Java | static vs 인스턴스 변수 차이점 (0) | 2025.10.04 |
Java | 업캐스팅과 다운캐스팅 (0) | 2025.10.02 |