JVMメモリの構造:ヒープを理解する
ヒープは、アプリケーションのすべてのオブジェクトが格納されるメモリ領域です。これは、いくつかの領域に分かれています。
ヒープの構造:
若い世代:
Eden Space — ここでは新しいオブジェクトが作成されます
Survivor Space (S0 と S1) — 最初のゴミ収集を乗り越えたオブジェクト
Old Generation (旧世代):
複数のガベージコレクションサイクルを生き延びた長寿命オブジェクト
Metaspace (Java 8+のPermGenに代わる):
クラスのメタデータ、定数
ヒープのサイズを設定する方法
# 最小および最大ヒープサイズを設定する
java -Xms2g -Xmx4g -jar myapp.jar
# -Xms — 初期ヒープサイズ (2 GB)
# -Xmx — 最大ヒープサイズ (4 GB)重要なルール: Xms と Xmx は、実行中にヒープのサイズが変わらないように、本番環境では同じサイズにすることをお勧めします。
必要なサイズを計算する方法は?
まずは次の式を使用します。
Heap Size = (Peak Live Data Size) × (2-4)Peak Live Data Sizeは、Full GC後の「ライブ」オブジェクトの最大量です。
ガベージコレクション:選択と設定
GC(ガベージコレクター)は、使用していないオブジェクトをメモリから自動的に削除します。ガベージコレクターにはいくつかの種類があり、それぞれに独自の特徴があります。
GCの主な種類:
1.シリアルGC (小規模アプリケーション向け)
java -XX:+UseSerialGC -jar myapp.jar使用するタイミング: 単一スレッドアプリケーション、ヒープ < 100 MB
2. Parallel GC(Java 8のデフォルト)
java -XX:+UseParallelGC -jar myapp.jar使用するタイミング: スループットが重要なアプリケーションでは、一時停止を許容できます
3. G1GC(ほとんどの場合に推奨)
java -XX:+UseG1GC -jar myapp.jar使用するタイミング: ヒープが4GBを超える場合、予測可能な一時停止が必要
一時停止の目標時間を設定するには:
java -XX:+UseG1GC -XX:MaxGCPauseMillis=200 -jar myapp.jar4. ZGCとShenandoah(低レイテンシーアプリケーション向け)
# ZGC(Java 11+、Java 15で本番環境対応)
java -XX:+UseZGC -jar myapp.jar
# Shenandoah
java -XX:+UseShenandoahGC -jar myapp.jar使用するタイミング: 最小ポーズ(<10 ms)、大きなヒープが重要です
ログでのGCの監視
詳細なGCログを有効にします。
java -Xlog:gc*:file=gc.log:time,uptime:filecount=5,filesize=100m \
-XX:+UseG1GC \
-jar myapp.jarプロファイリング:ボトルネックを見つける
プロファイリングは、アプリケーションが時間とメモリをどこで消費しているかを理解するのに役立ちます。
1. JVisualVMを使用する(無料)
JVisualVMはJDKに含まれており、次のことができます。
CPUとメモリの使用状況をリアルタイムで監視する
ヒープダンプを撮影する
フローを分析する
# JMXでアプリケーションを起動する
java -Dcom.sun.management.jmxremote \
-Dcom.sun.management.jmxremote.port=9010 \
-Dcom.sun.management.jmxremote.authenticate=false \
-Dcom.sun.management.jmxremote.ssl=false \
-jar myapp.jar次に、VisualVMを介してlocalhost:9010に接続します。
2. ヒープダンプの分析
OutOfMemoryErrorが発生した場合は、メモリのスナップショットを取得すると便利です。
# OOM時の自動ダンプ
java -XX:+HeapDumpOnOutOfMemoryError \
-XX:HeapDumpPath=/tmp/heapdump.hprof \
-jar myapp.jar
# または手動で作成する
jmap -dump:live,format=b,file=heap.bin <PID>Eclipse MATまたはVisualVMでダンプを分析し、次のものを探します。
最も多くのメモリを占有するオブジェクト
メモリリーク
予想外の大規模コレクション
3. Async Profiler (production-ready)
プロダクションには、アプリケーションの速度を低下させない async-profiler が適しています。
# ダウンロード
wget https://github.com/jvm-profiling-tools/async-profiler/releases/latest/download/async-profiler-2.9-linux-x64.tar.gz
# 60秒間プロファイリングを実行する
./profiler.sh -d 60 -f flamegraph.html <PID>結果は、フレームグラフで、「ホット」メソッドが表示されます。
4. メトリックのモニタリング
Micrometer + Prometheus + Grafanaを使用します。
// Spring Boot は JVM メトリクスを自動的にエクスポートします
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}application.propertiesに次を追加します。
management.endpoints.web.exposure.include=prometheus,health,metrics
management.metrics.export.prometheus.enabled=true
典型的な問題とその解決策
問題1:頻繁なFull GC
症状: アプリが数秒間「フリーズ」する
解決策:
# ヒープを増やすか、ヤングジェネレーションを設定してください
java -Xms4g -Xmx4g \
-XX:NewRatio=2 \
-XX:+UseG1GC \
-jar myapp.jar問題2:OutOfMemoryError:Javaヒープスペース
解決策:
-Xmxを拡大するヒープダンプを介してメモリリークを見つける
コードを最適化する(不要なコレクションを削除する)
問題3:OutOfMemoryError:メタスペース
解決策:
# メタスペースを拡大する
java -XX:MetaspaceSize=256m -XX:MaxMetaspaceSize=512m -jar myapp.jar問題 4:GC による高い CPU 使用率
解決策:
より効率的なGC(G1、ZGC)に切り替えます
ヒープを増やす
コード内のオブジェクトの作成を最適化する
プロダクション準備チェックリスト
1. メモリ設定
java -Xms4g -Xmx4g \ # 固定ヒープ
-XX:MetaspaceSize=256m \ # Metaspace
-XX:MaxMetaspaceSize=512m \
-XX:+UseG1GC \ # G1 GC
-XX:MaxGCPauseMillis=200 \ # 一時停止の目標時間
-XX:+HeapDumpOnOutOfMemoryError \ # OOM でのダンプ
-XX:HeapDumpPath=/var/log/app \
-jar myapp.jar2. GCのログ
-Xlog:gc*:file=/var/log/app/gc.log:time,uptime:filecount=5,filesize=100m3. 監視のためのJMX
-Dcom.sun.management.jmxremote \
-Dcom.sun.management.jmxremote.port=9010 \
-Dcom.sun.management.jmxremote.authenticate=true \
-Dcom.sun.management.jmxremote.ssl=true \
-Dcom.sun.management.jmxremote.password.file=/etc/jmx.password4. コンテナ化(Docker)
JVMは、Java 10以降のコンテナの制限を自動的に検出します。
FROM openjdk:17-slim
# JVMは自動的に制限を表示します
ENV JAVA_OPTS="-XX:+UseContainerSupport \
-XX:MaxRAMPercentage=75.0 \
-XX:+UseG1GC"
CMD java $JAVA_OPTS -jar app.jarCODE_BLOCK_19__5. プロダクションのモニタリング
必ず以下を追跡してください。
Heap usage (使用メモリ)
GC frequency (組み立て頻度)
GC pause time (休止時間)
Thread count (ストリーム数)
CPU usage
完成した構成の例
典型的なSpring Bootアプリケーションの場合:
#!/bin/bash
java -Xms2g -Xmx2g \
-XX:MetaspaceSize=256m \
-XX:MaxMetaspaceSize=256m \
-XX:+UseG1GC \
-XX:MaxGCPauseMillis=200 \
-XX:+HeapDumpOnOutOfMemoryError \
-XX:HeapDumpPath=/var/log/myapp/heapdump.hprof \
-Xlog:gc*:file=/var/log/myapp/gc.log:time,uptime:filecount=10,filesize=100m \
-Dcom.sun.management.jmxremote \
-Dcom.sun.management.jmxremote.port=9010 \
-Dcom.sun.management.jmxremote.authenticate=false \
-Dcom.sun.management.jmxremote.ssl=false \
-jar myapp.jar追加ツール
GCEasy (gceasy.io) — GCログのオンライン分析
FastThread (fastthread.io)—スレッドダンプの分析
JProfiler — 商業プロファイラー
YourKit — もう一つの優れた商用プロファイラー
Arthas — ランタイム診断のための Alibaba のツール
結論
JVMの最適化は反復的なプロセスです。基本的な概念(ヒープ、GC、プロファイリング)を理解することから始め、適切な診断ツールを使用し、特定の負荷に合わせて徐々に最適化します。
覚えておいてください: 早すぎる最適化はすべての悪の根源です。まずは測定してから最適化しましょう!
役に立ちましたか?
参加する コディク — 開発者向けの教育プラットフォーム!
ここでは、Java、パフォーマンスの最適化、本番環境の準備に関する構造化されたコース、実践的なタスク、および関連資料をご覧いただけます。
また、当社には 急な テレグラムチャンネル フレンドリーなコミュニティ経験豊富な開発者が知識を共有し、問題を解決し、ベストプラクティスについて話し合う場所です。 参加してください!
