このトピックでは、Application Real-Time Monitoring Service (ARMS) のサブサービスであるアプリケーションモニタリングでサポートされている Java 仮想マシン (JVM) のメモリメトリクスについて説明します。
Java プロセスのメモリ領域
次の図は、Java プロセスのメモリ領域を示しています。
JVM メカニズムの複雑さのため、この図は主要なメモリ領域のみを示しています。
ARMS が JVM メモリの詳細を取得する方法
ARMS エージェントは、Java Development Kit (JDK) が提供する MemoryMXBean を使用して、JVM ランタイム中にメモリ詳細を取得します。MemoryMXBean の制限により、ARMS の JVM メモリ監視機能は、Java プロセスが占有するすべてのメモリ領域をカバーできません。詳細については、Interface MemoryMXBean を参照してください。
ヒープメモリ
Java ヒープは、すべてのオブジェクトにメモリが割り当てられ、ガベージコレクション (GC) が実装されるコア Java メモリ領域です。Java ガベージコレクターによっては、ARMS でサポートされる最大許容ヒープメモリは、ユーザー定義のヒープメモリの上限よりわずかに小さくなる場合があります。Parallel Garbage Collector (ParallelGC) を例にとります。-XX:+UseParallelGC -Xms4096m -Xmx4096m
パラメーターを設定した場合、次の図に示すように、最大許容メモリサイズ (3.8 GB) はユーザー定義のメモリサイズ (4 GB) よりわずかに小さくなります。これは、MemoryMXBean に From Space 領域と To Space 領域のデータが含まれていないためです。
一般に、Garbage-First (G1) Garbage Collector を使用する場合、ARMS でサポートされる最大許容ヒープメモリは、ユーザー定義の -Xmx
または -XX:MaxRAMPercentage
パラメーターと一致します。ただし、ParallelGC、Concurrent Mark Sweep (CMS) Collector、または Serial Garbage Collector を使用する場合、わずかなずれがあります。
メタスペース
メタスペースは、クラス構造情報、メソッド情報、フィールド情報などのクラスメタデータを格納するために使用されます。メタスペースの使用量は一般的に安定しています。
非ヒープメモリ
ARMS でサポートされている非ヒープメモリには、メタスペース、圧縮クラス空間、コードキャッシュが含まれます。MemoryMXBean の制限により、ARMS はすべての非ヒープメモリ領域をサポートしていません。VM スレッドスタックと Java Native Interface (JNI) はサポートされていません。
メタスペース: クラスメタデータを格納します。JDK 8 以降のバージョンでは、
-XX:MetaspaceSize=N
および-XX:MaxMetaspaceSize=N
を設定することで、メタスペースのデフォルトサイズと最大サイズを指定できます。圧縮クラス空間: ロードされたクラスメタデータを圧縮して格納します。特別な JVM メモリ領域として、圧縮クラス空間はポインターの空間を制限することで Java アプリケーションのメモリ使用量を削減します。圧縮クラス空間は、JVM 起動パラメーター
-XX:CompressedClassSpaceSize
で指定できます。JDK 11 では、デフォルトの圧縮クラス空間サイズは 1 GB です。コードキャッシュ: JVM によって生成されたネイティブコードを格納します。インタプリタループ、JNI、Just-In-Time (JIT) コンパイル、Java メソッドなど、さまざまなソースがネイティブコードを生成します。JIT コンパイルによって生成されたネイティブコードは、コードキャッシュ空間の大部分を占めます。コードキャッシュの初期サイズと最大サイズは、JVM 起動パラメーター
-XX:InitialCodeCacheSize
および-XX:ReservedCodeCacheSize
で指定できます。
ダイレクトバッファ
Java ダイレクトバッファは、JVM のヒープメモリではなく、オペレーティングシステムのメモリに直接空間を割り当てる特別なバッファです。ダイレクトバッファは、より高速な I/O 操作を提供し、メモリコピーのオーバーヘッドを防ぎ、大量のデータを効率的に処理できます。多数の I/O 操作により、ダイレクトバッファメモリが増加します。
ヒープメモリリーク分析
ARMS は、ヒープメモリリークの包括的な分析機能を提供します。JVM ヒープメモリ監視機能を使用して、ヒープメモリがゆっくりと増加しているかどうかを確認できます。ヒープメモリが長時間増加し続ける場合は、ARMS が提供するメモリ スナップショットまたは継続プロファイリング機能を使用して、ヒープメモリリークのトラブルシューティングを行うことができます。
非ヒープメモリリーク分析
ヒープメモリが比較的安定しているのに、アプリケーションのメモリ全体が増加し続ける場合は、非ヒープメモリがリークしている可能性があります。ARMS は非ヒープメモリ分析を提供していません。Native Memory Tracking (NMT) ツールを使用して、非ヒープメモリ要求を監視できます。詳細については、Oracle のドキュメントを参照してください。
NMT には技術的な背景が必要であり、アプリケーションに 5% から 10% のパフォーマンスオーバーヘッドが発生します。ツールを使用する前に、オンラインアプリケーションへの影響を評価することをお勧めします。
メモリに関する FAQ
Q: ARMS コンソールのアプリケーションモニタリングモジュールに表示されるヒープメモリと非ヒープメモリの合計が、
top
コマンドを実行してクエリされた KiB 単位の常駐メモリサイズ (RES) と大きく異なるのはなぜですか?A: アプリケーションモニタリングによって収集されたデータは Java Management Extensions (JMX) から取得されたもので、VM スレッドスタック、ローカルスレッドスタック、および非 JVM メモリは除外されます。したがって、アプリケーションモニタリングによって提供される JVM メモリデータは、
top
コマンドを実行してクエリされた RES とは異なります。Q: ARMS コンソールのアプリケーションモニタリングモジュールに表示されるヒープメモリと非ヒープメモリの合計が、Managed Service for Prometheus および Managed Service for Grafana によって提供されるメモリデータと大きく異なるのはなぜですか?
A: アプリケーションモニタリングによって収集されたデータは JMX から取得されますが、Managed Service for Grafana によって提供されるメモリデータは、Prometheus Query Language を使用してクエリされたコンテナメトリクスから取得されます。一般に、これらのメトリクスの名前には container_memory_working_set_bytes が含まれています。実際には、常駐セットサイズ (RSS) とメモリ cgroup のアクティブキャッシュの合計がカウントされます。
Q: メモリ不足 (OOM) キラーが原因でポッドが再起動します。アプリケーションモニタリングを使用して問題をトラブルシューティングするにはどうすればよいですか?
A: アプリケーションモニタリングを使用して、ヒープメモリとダイレクトバッファのキャパシティプランニングの問題を簡単にトラブルシューティングできます。ただし、アプリケーションモニタリングが JMX から取得するメモリデータには、JVM プロセス全体の RSS 消費量の詳細は含まれていません。したがって、OOM キラーのトラブルシューティングには、Kubernetes の Prometheus 監視エコシステムを使用する必要があります。さらに、次の 2 つの点に注意してください。
ポッドには単一プロセスモデルしかないのですか?
他のプロセスがメモリを消費しているかどうかを確認する必要があります。
JVM プロセスの外部にリークが存在しますか?
たとえば、glibc がメモリリークを引き起こす可能性があります。