~Javaプログラムを“止まらず・軽く”動かすためのメモリ戦略~
はじめに:なぜ「GCログ+ヒープダンプ」が強いのか?
Javaのプログラム運用において、
「メモリが徐々に増えていく」「応答が遅くなる」「アプリが突然止まる」といった問題に遭遇することは珍しくありません。
これらの原因は多くの場合、ガベージコレクション(GC)やメモリリークにあります。
しかし、それらをただ漠然と「メモリ使いすぎだな」と片付けてしまうと、
根本的な原因が見えずに再発を繰り返します。
そこで役立つのが、**「GCログ」+「ヒープダンプ」**という2つの観察手段を組み合わせた調査です。
- GCログ:どのタイミングでGCが起きたか、どれだけ停止時間があったか、ヒープの使用量はどう変わったか。
- ヒープダンプ:その時点でヒープ中にどんなオブジェクトがどれだけいて、なぜ回収されないか。
これらを組み合わせることで、
「なぜGCが頻繁に起きるか」「なぜヒープが減らないか」「どのオブジェクトが原因か」
まで深掘りできます。そして、単なる対症療法ではなく、設計・運用レベルでの最適化に繋がります。
今回はその「実践編」として、具体的な手順・ポイント・コード例も含めて解説します。
全体流れ:4つのステップで進める
以下のような流れで進めると、調査から改善まで無駄なく実施できます。
- GCログを取得・観察する
- 異常を発見したらヒープダンプを取得・解析する
- 原因を特定し、コードまたは設定を改善する
- 再度ログ・ダンプを取得して、改善効果を検証する
このサイクルを回すことで、確実にメモリ最適化が進みます。
ステップ①:GCログを取得・観察する
GCログの取得方法
JavaバージョンやGC方式によってログ取得のオプションは異なりますが、基本的な例として:
- Java 8以前の場合(例:G1GC)
-XX:+PrintGCDetails -XX:+PrintGCDateStamps -Xloggc:/path/to/gc.log - Java 9以降の場合
-Xlog:gc*=info:file=/path/to/gc.log:time,uptime,level,tags
ログのローテーションやサイズ制限も忘れずに。
ログで注目すべきポイント
GCログを見て「異常だな」と感じる兆候には以下があります。
- GCが頻繁すぎる:オブジェクトを大量に生成・破棄して若世代が頻繁に回収されている。
- GC停止時間(STW:Stop The World)が長い:アプリが数百ミリ秒~数秒以上停止している。
- 古い世代(Old Generation)が増え続けて減らない:ヒープ使用量は戻らず、フルGCが発生している。
- アロケーション失敗(Allocation Failure)で頻繁にGCが起きている:若世代に割り当てる空きが無い。
例:ログから何が分かるか
|
1 2 3 |
[2025-05-10T12:34:56.789] GC(45) Pause Young (G1 Evacuation Pause) 70M->20M(100M) 8.234ms [2025-05-10T12:35:10.123] GC(46) Pause Full (Allocation Failure) 95M->50M(100M) 320.456ms |
この例から分かること:
- 若世代GCは8msと短くて許容範囲
- しかし直後にフルGCが320ms発生。ヒープがほぼ満杯(95M/100M)でアロケーション失敗 → 即・全体回収の流れ
- =若世代で回収できず、多くのオブジェクトが旧世代へ昇格 → フルGCの引き金に
このようなパターンを見つけたら、次のステップへ進む合図です。
ステップ②:ヒープダンプを取得・解析する
GCログで「怪しい」と感じたら、ヒープダンプを取得して具体的に原因を突き止めましょう。
ヒープダンプの取得方法
- JVMオプションで自動取得:
-XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/path/to/dump.hprof - 実行中プロセスから:
jcmd <pid> GC.heap_dump filename=heap_dump.hprof
ツールでの解析(例:Eclipse Memory Analyzer (MAT))
ダンプ取得後は以下のように解析します。
- ダンプをMATで開く
- 「Leak Suspects Report」で怪しいクラスをピックアップ
- “Histogram”でインスタンス数・サイズを確認
- “Dominator Tree”でどのオブジェクトが大きな保持量を持っているか分析
ログとの組み合わせで見えること
- GCログで「旧世代増加/フルGC頻発」を確認
- ヒープダンプで「どのクラスのインスタンスが増えているか」「どこから参照されているか」確認
- これにより、「なぜ旧世代が増えたか」「どのオブジェクトが放置されていたか」が分かる
ステップ③:原因特定と改善策
原因に応じて、コード修正・設定変更を行います。以下は代表的な改善パターンです。
ケースA:短命オブジェクトが多すぎて若世代が飽和
- 若世代が頻回にGCされ、旧世代へ昇格してしまう
- → ログ:若GC頻発+旧世代使用率増加
- → 対策:
- オブジェクト生成量を減らす(再利用可能なオブジェクトを検討)
- 若世代サイズを調整 (
-XX:NewSize,-XX:MaxNewSize) - GC方式を見直す(例:G1GCなら
-XX:InitiatingHeapOccupancyPercentを調整)
ケースB:メモリリークで旧世代が戻らない
- ヒープダンプで「大量のインスタンスが参照され続けている」クラスを確認
- → ログ:フルGCしてもヒープが減らない/使用率上昇のまま
- → 対策:
- 不要な参照を切る(static、コレクション、リスナーなど)
- キャッシュ設計を見直す(期限付き、弱参照)
- ThreadLocal やクラスローダーのリークをチェック
ケースC:ポーズ時間(STW)が長すぎて応答に影響
- ログ:STWが数百ミリ秒以上/応答遅延が出ている
- → 対策:
- 小さめのヒープ/リージョン設計にする
- 低遅延GC方式(例:ZGC)を検討
- ヒープ断片化の対策、オブジェクト寿命別分離
ステップ④:再検証と運用モニタリング
修正後、必ず再評価を行いましょう。
- 修正前後でGCログを比較:GC頻度・ポーズ時間・旧世代使用率が改善されているか
- 再度ヒープダンプを取得:問題クラスのインスタンス数・保持量が減っているか
- 運用中もモニタリングを継続:
- ヒープ使用量の推移(安定しているか)
- GC停止時間の傾向
- 応答時間・スループットとの相関
このように「改善 → 検証 →運用監視」を回すことで、メモリ最適化が定着します。
初心者プログラマー向けポイント
- まずは“ログを読む”ことに慣れよう。
GCログを眺めれば「何か増えてるな/止まってるな」が徐々にわかるようになります。 - ツール(MAT)を使うと“オブジェクトが残っている理由”が可視化できます。
- 最初から完璧にする必要はありません。小さな改善(キャッシュのクリア、static参照の整理)から実施しましょう。
- 学習は理論だけでなく「実際に自分でログを取る」「ダンプを開く」ことが大事です。
- まずは書籍で基礎を学び、実践に移ると早く身につきます。
→ 絶対にJavaプログラマーになりたい人へ。
→ コードレビューや転職支援も考えるなら サイゼントアカデミー へ。
まとめ
- GCログとヒープダンプを組み合わせることで、原因の“見えない壁”を可視化できる。
- 調査→分析→改善→再検証というサイクルを回すことが、メモリ最適化の実践パターン。
- 「GCが頻繁」「ヒープが戻らない」「ポーズが長い」と感じたら、まずログを見て、そのあとダンプを使って深掘り。
- 小さな改善が、長時間稼働するシステムでは大きな差になる。
次回は、「クラウド環境(コンテナ・Kubernetes)でのGCログ&ヒープダンプ活用の実践」についてお届けします。
あなたのJavaスキルがさらに深まることを願っています。

でのGCログ&ヒープダンプ活用の実践-120x68.jpg)
コメント