メモリ管理
はじめに
JVMのメモリ管理を理解することは、アプリケーションのパフォーマンス最適化とトラブルシューティングに不可欠です。この章では、JVMの各メモリ領域の役割と特徴を解説します。
JVMメモリ構造の全体像
┌─────────────────────────────────────────────────────────────────────┐
│ JVM Process │
├─────────────────────────────────────────────────────────────────────┤
│ │
│ ┌────────────────────────────────────────────────────────────────┐ │
│ │ Heap │ │
│ │ ┌─────────────────────────┐ ┌─────────────────────────────┐ │ │
│ │ │ Young Generation │ │ Old Generation │ │ │
│ │ │ ┌─────┐ ┌────┐ ┌────┐ │ │ │ │ │
│ │ │ │Eden │ │ S0 │ │ S1 │ │ │ Long-lived objects │ │ │
│ │ │ └─────┘ └────┘ └────┘ │ │ │ │ │
│ │ └─────────────────────────┘ └─────────────────────────────┘ │ │
│ │ -Xms / -Xmx │ │
│ └────────────────────────────────────────────────────────────────┘ │
│ │
│ ┌────────────────────────────────────────────────────────────────┐ │
│ │ Metaspace │ │
│ │ クラスメタデータ、定数プール、メソッド情報 │ │
│ │ -XX:MaxMetaspaceSize │ │
│ └────────────────────────────────────────────────────────────────┘ │
│ │
│ ┌───────────────────┐ ┌───────────────────┐ ┌─────────────────┐ │
│ │ Thread Stacks │ │ Code Cache │ │ Direct Memory │ │
│ │ -Xss │ │ JIT compiled │ │ NIO buffers │ │
│ └───────────────────┘ └───────────────────┘ └─────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────┘
ヒープ(Heap)
概要
ヒープは、オブジェクトインスタンスと配列が格納される領域です。GC(ガベージコレクション)の対象となります。
世代別構成
┌─────────────────────────────────────────────────────────────────────┐
│ Heap │
├─────────────────────────────────────── ──────────────────────────────┤
│ │
│ Young Generation(若い世代) │
│ ┌────────────────────────────────────────────────────────────────┐ │
│ │ │ │
│ │ ┌──────────────────────────────┐ │ │
│ │ │ Eden │ 新しいオブジェクトはここに │ │
│ │ │ │ 割り当てられる │ │
│ │ └──────────────────────────────┘ │ │
│ │ │ │
│ │ ┌─────── ─────────┐ ┌────────────────┐ │ │
│ │ │ Survivor 0 │ │ Survivor 1 │ Eden から生き残った │ │
│ │ │ (From) │ │ (To) │ オブジェクトがコピー │ │
│ │ └────────────────┘ └────────────────┘ │ │
│ │ │ │
│ └────────────────────────────────────────────────────────────────┘ │
│ │
│ Old Generation(古い世代) │
│ ┌────────────────────────────────────────────────────────────────┐ │
│ │ │ │
│ │ 長期間生存したオブジェクト │ │
│ │ (Survivor を何度か経由したもの) │ │
│ │ │ │
│ └────────────────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────┘
オブジェクトのライフサイクル
1. new Object()
│
↓
Eden に割り当て
│
↓ (Minor GC発生)
│
┌───┴───┐
│ │
生存 死亡 → 回収
│
↓
Survivor へコピー
(年齢 +1)
│
↓ (Minor GC繰り返し)
│
年齢が閾値に達する
(デフォルト: 15)
│
↓
Old Generation へ昇格
│
↓ (Major GC発生)
│
┌───┴───┐
│ │
生存 死亡 → 回収
ヒープサイズの設定
# 初期ヒープサイズと最大ヒープサイズ
java -Xms512m -Xmx2g -jar app.jar
# 同じ値に設定するのが推奨(動的リサイズのオーバーヘッド回避)
java -Xms2g -Xmx2g -jar app.jar
| オプション | 説明 | 推奨設定 |
|---|---|---|
-Xms | 初期ヒープサイズ | -Xmxと同じ値 |
-Xmx | 最大ヒープサイズ | 物理メモリの50-70% |
-Xmn | Young Generationサイズ | 通常は自動調整に任せる |
世代別サイズ比率
# Young Generation の割合を指定
java -XX:NewRatio=2 -jar app.jar # Old:Young = 2:1
# Survivor の割合を指定
java -XX:SurvivorRatio=8 -jar app.jar # Eden:S0:S1 = 8:1:1
スタック(Stack)
概要
各スレッドには専用のスタックがあり、メソッド呼び出しごとにスタックフレームが積まれます。
┌─────────────────────────────────────────────────────────────────────┐
│ Thread Stack │
├───────────────────────────────────────────────────────────────────── ┤
│ │
│ ┌────────────────────────────────────────────────────────────────┐ │
│ │ Stack Frame (methodC) │ │
│ │ ┌──────────────┐ ┌──────────────┐ ┌──────────────────────┐ │ │
│ │ │ローカル変数 │ │オペランド │ │フレームデータ │ │ │
│ │ │テーブル │ │スタック │ │(戻りアドレス等) │ │ │
│ │ └──────────────┘ └──────────────┘ └──────────────────────┘ │ │
│ └────────────────────────────────────────────────────────────────┘ │
│ ┌────────────── ──────────────────────────────────────────────────┐ │
│ │ Stack Frame (methodB) │ │
│ │ ┌──────────────┐ ┌──────────────┐ ┌──────────────────────┐ │ │
│ │ │ローカル変数 │ │オペランド │ │フレームデータ │ │ │
│ │ │テーブル │ │スタック │ │ │ │ │
│ │ └──────────────┘ └──────────────┘ └──────────────────────┘ │ │
│ └────────────────────────────────────────────────────────────────┘ │
│ ┌────────────────────────────────────────────────────────────────┐ │
│ │ Stack Frame (methodA) │ │
│ │ ┌──────────────┐ ┌──────────────┐ ┌──────────────────────┐ │ │
│ │ │ローカル変数 │ │オペランド │ │フレームデータ │ │ │
│ │ │テーブル │ │スタック │ │ │ │ │
│ │ └──────────────┘ └──────────────┘ └──────────────────────┘ │ │
│ └────────────────────────────────────────────────────────────────┘ │
│ │
│ ↓ スタックの成長方向 │
│ │
└────────────────────────────────────────────────────────── ───────────┘
スタックのライフサイクル
スタックはスレッドと共に生成・破棄されます。
┌─────────────────────────────────────────────────────────────────────┐
│ スタックのライフサイクル │
├──────────────────────── ─────────────────────────────────────────────┤
│ │
│ 1. スレッド作成時 │
│ new Thread() / Executors.newThread() / Virtual Thread │
│ │ │
│ ↓ │
│ ┌─────────────────────────────────┐ │
│ │ スタック領域を確保 │ ← OSからメモリ割り当て │
│ │ (デフォルト: 1MB) │ (-Xss で指定) │
│ └─────────────────────────────────┘ │
│ │
│ 2. メソッド呼び出し時 │
│ method() を呼び出すたび │
│ │ │
│ ↓ │
│ ┌─────────────────────────────────┐ │
│ │ スタックフレームをプッシュ │ ← スタックポインタ移動 │
│ │ (ローカル変数、戻りアドレス等) │ │
│ └─────────────────────────────────┘ │
│ │
│ 3. メソッド終了時(return / 例外) │
│ method() から戻るたび │
│ │ │
│ ↓ │
│ ┌─────────────────────────────────┐ │
│ │ スタックフレームをポップ │ ← スタックポインタ移動 │
│ │ (メモリは即座に再利用可能) │ GC不要! │
│ └─────────────────────────────────┘ │
│ │
│ 4. スレッド終了時 │
│ run() 完了 / interrupt / 例外 │
│ │ │
│ ↓ │
│ ┌─────────────────────────────────┐ │
│ │ スタック領域を解放 │ ← OSにメモリ返却 │
│ │ (スレッド全体のスタック消滅) │ │
│ └─────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────┘