web-dev-qa-db-ja.com

Dockerコンテナで実行されているJVMの常駐セットサイズ(RSS)とJava合計コミットメモリ(NMT))の違い

シナリオ:

DockerコンテナでJVMを実行しています。 2つのツールを使用してメモリ分析を行いました。1)top2)Java Native Memory Tracking。数字は紛らわしいように見え、違いの原因を見つけようとしています。

質問:

RSSはJavaプロセスに対して1272MBとして報告され、合計Javaメモリは790.55 MBとして報告されます。メモリの残りの部分をどこで説明できますか。 1272-790.55 = 481.44 MB行きますか?

なぜSO:この質問 を見た後でもこの問題を開いたままにしておきたい

私は答えを見て、説明は理にかなっています。ただし、Java NMTおよびpmap -xから出力を取得した後、私はまだどのJavaメモリアドレスは実際に常駐し、物理的にマッピングされます。RSSとJava Totalコミットメモリの違いを引き起こすものを見つけるために、詳細な手順を含む) 。

トップ出力

enter image description here

Java NMT

enter image description here

Dockerメモリの統計

enter image description here

グラフ

Dockerコンテナを48時間以上実行しています。今、私が含むグラフを見たとき:

  1. Dockerコンテナに提供される合計メモリ= 2 GB
  2. Java最大ヒープ= 1 GB
  3. コミット合計(JVM)=常に800 MB未満
  4. 使用済みヒープ(JVM)=常に200 MB未満
  5. Non Heap Used(JVM)=常に100 MB未満。
  6. RSS =約1.1 GB。

では、1.1 GB(RSS)から800 MB(Javaの合計コミットメモリ)までのメモリを消費しているのは何ですか?

enter image description here

34
sunsin1985

" Analyzing Java Dockerコンテナのメモリ使用量Mikhail Krestjaninoff :に手がかりがあります。

(そして、明確にするために、3年後の2019年5月に 状況は改善しますopenJDK 8u212で

[〜#〜] r [〜#〜]esident[〜#〜] s [〜#〜] et[〜#〜] s [〜#〜]izeは、現在割り当てられ使用されている物理メモリの量です。プロセスによって(スワップアウトされたページなし)。コード、データ、および共有ライブラリ(これらを使用するすべてのプロセスでカウントされます)が含まれます

Dockerの統計情報がpsデータと異なるのはなぜですか?

最初の質問に対する答えは非常に簡単です- Dockerにはバグ(または機能-気分によって異なります) :合計メモリ使用量情報にファイルキャッシュが含まれます。そのため、このメトリックを回避して、RSSに関するps情報を使用できます。

さて、OK-しかしなぜRSSはXmxよりも高いのでしょうか?

理論的には、Javaアプリケーションの場合

RSS = Heap size + MetaSpace + OffHeap size

offHeapは、スレッドスタック、ダイレクトバッファ、マッピングされたファイル(ライブラリとjar)、およびJVMコードで構成されます

JDK 1.8.4 なので、ネイティブメモリトラッカー

ご覧のとおり、私はすでに-XX:NativeMemoryTracking=summaryプロパティをJVMに追加するため、コマンドラインから呼び出すことができます。

docker exec my-app jcmd 1 VM.native_memory summary

(これはOPがしたことです)

「不明」セクションについては心配しないでください-NMTは未熟なツールであり、CMS GCを処理できないようです(別のGCを使用するとこのセクションは表示されなくなります)。

NMTは「ps」コマンドで取得する「常駐」ではなく「コミット済み」メモリを表示することに注意してください。つまり、メモリページは、(直接アクセスされるまで)常駐と見なさずにコミットできます

これは、ヒープ以外の領域(ヒープは常に事前に初期化されている)のNMT結果がRSS値よりも大きい可能性があることを意味します

(つまり、「 JVMがLinuxプロセスの常駐セットサイズより多くのコミットメモリを報告するのはなぜですか? が入ります)」

その結果、jvmヒープ制限を256mに設定したにもかかわらず、アプリケーションは367Mを消費します。 「その他」の164Mは、主にクラスメタデータ、コンパイル済みコード、スレッド、GCデータの保存に使用されます。

多くの場合、最初の3つのポイントはアプリケーションの定数であるため、ヒープサイズとともに増加するのはGCデータのみです。
この依存関係は線形ですが、「k」係数(y = kx + b)は1より小さい。


より一般的には、これに issue 1502 が続くようです。これは、docker 1.7以降の同様の問題を報告します。

単純なScala(JVM)アプリケーションを実行しています。このアプリケーションは、大量のデータをメモリにロードしたり、メモリからロードしたりします。
JVMを8Gヒープに設定しました(-Xmx8G)。 132Gのメモリを搭載したマシンがありますが、JVMに課せられた8Gの制限をはるかに超えて成長するため、7〜8を超えるコンテナを処理できません。

docker stat だった 以前は誤解を招くと報告されていた 、明らかにメモリ使用量の合計にファイルキャッシュが含まれているため)

docker stat は、各コンテナ自体がJVMが使用するはずのメモリよりもはるかに多くのメモリを使用していることを示しています。例えば:

CONTAINER CPU % MEM USAGE/LIMIT MEM % NET I/O
dave-1 3.55% 10.61 GB/135.3 GB 7.85% 7.132 MB/959.9 MB
perf-1 3.63% 16.51 GB/135.3 GB 12.21% 30.71 MB/5.115 GB

JVMはコンテナ内に割り当てられているメモリをOSに要求しており、JVMはGCの実行時にメモリを解放しているようですが、コンテナはメモリを解放しませんメインOSに。だから...メモリリーク。

33
VonC