Javaのメモリ不足トラブルを調査していると、
- ヒープは空いているのにOOM
- Metaspace OOMで突然起動しなくなる
- Direct Memoryが原因でOSのOOM Killerが動く
といった現象に必ず遭遇します。
その原因の多くは、Javaのメモリ領域の違いを正しく理解していないことです。
目次
Javaメモリは「ヒープだけ」ではない
まず前提として、
Javaプロセスが使うメモリ = Javaヒープ
ではありません。
実際には、主に以下の領域があります。
- Java Heap(ヒープ)
- Metaspace
- Direct Memory(ヒープ外メモリ)
これらは役割・制御方法・OOMの出方が全て異なります。
① Javaヒープ(Heap)とは
役割
Javaヒープは、
- new で生成したオブジェクト
- アプリケーションデータ
が格納される、最も基本的なメモリ領域です。
制御方法
-Xms2g # 初期ヒープサイズ
-Xmx4g # 最大ヒープサイズ
OOMの種類
java.lang.OutOfMemoryError: Java heap space
特徴
- GC(Garbage Collection)の対象
- サイズは明示的に制御可能
- 最も調整しやすい
② Metaspaceとは
役割
Metaspaceは、
- クラス情報
- メソッド定義
- リフレクション関連情報
を保持する領域です。
Java 8以降では、PermGenの代わりとして導入されました。
重要ポイント
Metaspaceはヒープではありません。
つまり、
- -Xmx の対象外
- OSの物理メモリを直接消費
します。
制御方法
-XX:MetaspaceSize=256m
-XX:MaxMetaspaceSize=512m
OOMの種類
java.lang.OutOfMemoryError: Metaspace
典型的な原因
- 大量のクラスロード
- クラスローダリーク
- アプリの再デプロイ繰り返し
③ Direct Memory(ヒープ外メモリ)とは
役割
Direct Memoryは、
- NIO
- Netty
- Kafka / Elasticsearch クライアント
などが使用するネイティブメモリです。
Javaヒープを経由せず、 OSのメモリを直接確保します。
制御方法
-XX:MaxDirectMemorySize=512m
OOMの出方
JVM内部で発生する場合:
java.lang.OutOfMemoryError: Direct buffer memory
OS側で発生する場合:
- Javaログに何も残らない
- OOM Killerでプロセスが即死
最大の注意点
GCでは回収されにくい
ため、
- ヒープは空いている
- でも物理メモリが枯渇
という状況を引き起こします。
3つの領域の比較表
| 項目 | Heap | Metaspace | Direct Memory |
|---|---|---|---|
| 主用途 | オブジェクト | クラス情報 | I/O・高速通信 |
| 制御オプション | -Xmx | -XX:MaxMetaspaceSize | -XX:MaxDirectMemorySize |
| GC対象 | ◯ | △ | × |
| OOMの見え方 | ログに出る | ログに出る | 出ないことが多い |
よくある事故パターン
① ヒープしか見ていない
- Xmxは余裕あり
- Metaspace / Direct Memory無制限
→ OSのOOM Killer発動
② systemd / cgroup制限と衝突
- MemoryMaxに収まらない
- Swapがあっても即OOM
実務での確認コマンド
JVMメモリ内訳
jcmd <PID> VM.native_memory summary
プロセスの実メモリ使用量
ps -o pid,rss,vsz,cmd -p <PID>
安全な設計指針
- Heapだけでなく全領域を設計に含める
- Metaspace / Direct Memory は必ず上限指定
- systemd / コンテナ制限を事前確認
- Swapに依存しない
まとめ
- Javaメモリは「ヒープだけ」ではない
- MetaspaceとDirect MemoryはOSメモリ直結
- OOMの多くはヒープ外が原因
- 正しい理解が障害回避の第一歩
JavaのOOM対策は、 メモリ構造を知った瞬間に一気に楽になります。
あわせて読みたい


SwapがあるのにJavaがOOMで落ちる理由【よくある誤解と正しい理解】
「Swapは十分にあるのに、なぜかJavaプロセスがOOMで落ちる」この現象は、Java × Linux 障害対応で非常によくある誤解です。 結論から言うと、 Swapがある=JavaがOOMに…
あわせて読みたい


LinuxのOOM Killerが発動する条件と回避策【実務での完全理解】
Linuxを運用していると、ある日突然アプリケーションが終了し、 ログを確認すると OOM Killer が動いていた── という経験をした方も多いのではないでしょうか。 OOM Kil…
あわせて読みたい


systemd MemoryMaxとJavaメモリ設計の落とし穴
Linux環境でJavaアプリをsystemd管理している場合、 MemoryMaxの設定が原因で予期せぬOOMやプロセス強制終了が発生するケースが非常に多くあります。 本記事では、syste…
あわせて読みたい


KubernetesでJavaがOOMKilledされる仕組み
Kubernetes(K8s)上でJavaアプリを運用していると、 java.lang.OutOfMemoryErrorが出ないまま Pod が突然再起動する という現象に遭遇することがあります。 これは多く…
