メモリーリークの検出と修正
よくあるメモリーリークのパターン(static コレクション、リスナー、キャッシュ)を特定し、修正します。
「メモリーリークの検出と修正」はCoddyKit上の無料Java Academyレッスンです。 これはレッスン3/4です。 下記で完全なレッスンを無料で読むことができます。その後、ブラウザ内の組み込みコードエディタと24時間対応のAIチューターでハンズオン演習できます。 これはJava Academy学習パスの一部であり、ウェブとCoddyKitアプリ全体で進捗が同期されます。 Java Academyコースには全4レッスンが含まれています。
Javaのメモリリークとは
Javaのメモリリークは、不要になったオブジェクトが強く到達可能な状態のまま残り、GCによる回収を妨げることで発生します。OutOfMemoryErrorがスローされるまでヒープが増加します。
静的コレクションの蓄積
増え続けるコレクションを保持するstaticフィールドは、典型的なリークです。追加されたものの削除されないオブジェクトは、アプリケーションの存続期間中ずっと生き続けます。
public class Cache {
// LEAK: static list grows forever if items are never removed
private static final List<Object> items = new ArrayList<>();
public static void add(Object o) { items.add(o); }
// Fix: add a remove() method or use a bounded cache
}登録解除されないイベントリスナー
リスナーを登録したまま削除しないと、リスナー自身と、リスナーが参照するオブジェクトが生存し続けます。不要になったリスナーは必ず登録解除してください。
// Leak:
sensor.addListener(new DataLogger());
// Fix:
DataLogger logger = new DataLogger();
sensor.addListener(logger);
// ... when done:
sensor.removeListener(logger);クリーンアップされないThreadLocal変数
スレッドプールではスレッドが再利用されるため、ThreadLocalの値がタスク間で残ります。クリアしないと、あるタスクのデータが同じスレッドで後続のタスクに漏れ出します。
private static final ThreadLocal<MyContext> CTX = new ThreadLocal<>();
// Always clean up after each task:
try {
CTX.set(new MyContext(requestId));
doWork();
} finally {
CTX.remove(); // prevents leak in pooled threads
}ホットデプロイされるアプリでのクラスローダーリーク
サーブレットコンテナでは、デプロイごとに新しいクラスローダーが使用されます。いずれかのクラスが古いクラスローダーのクラスへのstatic参照を保持すると、古いクラスローダー全体(およびそのすべてのクラス)がGCの対象になりません。
ヒープダンプでリークを検出する
jmap -dump:format=b,file=heap.hprof <pid> でヒープダンプを取得し、Eclipse MAT または VisualVM で開いて、保持サイズが最大のオブジェクトとその GC ルートを見つけます。
// Take a heap dump:
jmap -dump:live,format=b,file=heap.hprof $(jps | grep MyApp | cut -d" " -f1)
// Or trigger on OOM:
// -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/tmp/heap.hprofEclipse Memory Analyzer (MAT)
MAT の「Leak Suspects」レポートは、保持ヒープが大きいオブジェクトを自動的に特定し、GC ルートからのパスを表示します。まず「Dominator Tree」ビューで、最も大きな原因となっているオブジェクトを見つけます。
WeakReference でリークを回避する
キャッシュ対象やリスナーオブジェクトを WeakReference でラップします。メモリ不足時には GC がそれらを回収できます。必ず get() の戻り値が null でないか確認してください。
Map<String, WeakReference<Image>> imageCache = new HashMap<>();
imageCache.put("logo", new WeakReference<>(loadImage("logo.png")));
Image logo = imageCache.get("logo") != null ? imageCache.get("logo").get() : null;
if (logo == null) logo = reload("logo.png"); // re-load if GCedLinkedHashMap LRU によるサイズ制限付きキャッシュ
LinkedHashMap の removeEldestEntry をオーバーライドしてキャッシュサイズに上限を設けると、最も長く使われていないエントリを自動的に追い出せます。
Map<String, String> lruCache = new LinkedHashMap<>(16, 0.75f, true) {
protected boolean removeEldestEntry(Map.Entry<String, String> e) {
return size() > 100; // evict when over 100 entries
}
};Java Flight Recorder で割り当てをプロファイリングする
JFR (Java Flight Recorder) は、オーバーヘッドを最小限に抑えながら割り当てのプロファイルを取得します。-XX:StartFlightRecording で有効にし、JDK Mission Control で分析します。
// Start a 60-second JFR recording:
java -XX:StartFlightRecording=duration=60s,filename=rec.jfr MyApp
// Or via jcmd:
jcmd <pid> JFR.start duration=60s filename=rec.jfrリークを修正するためのチェックリスト
次の項目を確認します: static コレクション、登録解除されていないリスナー、プール内の ThreadLocal、接続やストリームのリーク(try-with-resources を使用)、外側のオブジェクトへの内部クラスからの参照、追い出し処理のないキャッシュ。
確認問題
OutOfMemoryError がスローされたときにヒープを自動ダンプする JVM フラグは何ですか。
まとめ
Java でよくあるリークの原因は、static コレクション、登録解除されていないリスナー、プールされた ThreadLocal、上限のないキャッシュです。ヒープダンプと MAT で検出し、WeakReference、サイズ制限付きのデータ構造、不要になった参照の確実な削除で修正します。
よくある質問
「メモリーリークの検出と修正」レッスンは無料ですか?
はい。「メモリーリークの検出と修正」の完全なテキストはこのウェブで無料で読めます。インタラクティブに演習し(組み込みコードエディタと24時間対応のAIチューター)、Java Academyコースの残りをアンロックするには、CoddyKit PROにアップグレードしてください。 Java Academyコースには全4レッスンが含まれています。
「メモリーリークの検出と修正」で何を学びますか?
よくあるメモリーリークのパターン(static コレクション、リスナー、キャッシュ)を特定し、修正します。 ブラウザで直接実行するハンズオンコードでJava Academyを演習し、24時間対応のAIチューターがレッスンを進める中での質問に答えます。
Java Academyを始めるのに経験は必要ですか?
事前経験は必要ありません。CoddyKitのJava Academyは初級者から上級者向けに構成されているため、ここから始めるか最初から始めて、自分のペースで進むことができます。 これはレッスン3/4です。
「メモリーリークの検出と修正」レッスンにはどのくらい時間がかかりますか?
ほとんどのCoddyKitレッスンは約5~10分かかります。各レッスンはコンパクトでインタラクティブなので、着実に進歩し、ウェブとアプリ全体で正確に前回の場所から再開できます。
このJava Academyレッスンでコードを書いて実行できますか?
はい。すべてのJava Academyレッスンに組み込みコードエディタが含まれているため、ブラウザでリアルコードを書いて実行し、即座のAIフィードバックを取得できます。ローカル設定は不要です。
このコースのすべてのレッスン
- JVM ヒープ領域とオブジェクトのライフサイクル
- GC アルゴリズム:Serial、G1、ZGC、Shenandoah
- メモリーリークの検出と修正
- GC チューニングフラグと JVisualVM によるプロファイリング