web-dev-qa-db-ja.com

Javaプロセスを実行しているdockerによって消費される原因不明の追加メモリ

私たちが持っているもの:

  • Dockerコンテナーで実行されているJavaアプリ
  • ネイティブコードは呼び出されず、処理も起動されず、DLL/.soファイルは参照されません。
  • JVM引数:-Xmx256m -XX:NativeMemoryTracking=summary
  • Dockerハードメモリ制限を768mに設定
  • JVMは正常に見える(通常のGCサイクル、メモリリーク、OOMなし)
  • Dockerメモリは、ハードリミット(768m)に達するまで増大し続け、その結果、コンテナが強制終了され、再起動されます。

問題 :

  • JVMが制限内で動作しているように見えるにもかかわらず、Docker統計メモリが増加し続ける(ハードメモリ制限が毎日ヒットする)のはなぜですか?.

enter image description here

  • 他のマイクロサービスでは、この動作は見られません

enter image description here

JVM

JVM側では、特別なものは何もありません。

enter image description here

Docker stats出力:

492.8MiB / 768MiB     64.17%              

[ec2-user@ip-10-180-28-222 ~]$ docker exec 34d7 jcmd 1 VM.native_memory summary
1:

Native Memory Tracking:

Total: reserved=1731355KB, committed=472227KB
-                 Java Heap (reserved=262144KB, committed=262144KB)
                            (mmap: reserved=262144KB, committed=262144KB)

-                     Class (reserved=1131805KB, committed=92829KB)
                            (classes #16224)
                            (malloc=7453KB #20996)
                            (mmap: reserved=1124352KB, committed=85376KB)

-                    Thread (reserved=29932KB, committed=29932KB)
                            (thread #30)
                            (stack: reserved=29772KB, committed=29772KB)
                            (malloc=94KB #151)
                            (arena=66KB #55)

-                      Code (reserved=255659KB, committed=35507KB)
                            (malloc=6059KB #9814)
                            (mmap: reserved=249600KB, committed=29448KB)

-                        GC (reserved=15369KB, committed=15369KB)
                            (malloc=5785KB #547)
                            (mmap: reserved=9584KB, committed=9584KB)

-                  Compiler (reserved=190KB, committed=190KB)
                            (malloc=59KB #858)
                            (arena=131KB #6)

-                  Internal (reserved=7849KB, committed=7849KB)
                            (malloc=7817KB #18468)
                            (mmap: reserved=32KB, committed=32KB)

-                    Symbol (reserved=20018KB, committed=20018KB)
                            (malloc=17325KB #175818)
                            (arena=2693KB #1)

-    Native Memory Tracking (reserved=3558KB, committed=3558KB)
                            (malloc=10KB #120)
                            (tracking overhead=3548KB)

-               Arena Chunk (reserved=4830KB, committed=4830KB)
                            (malloc=4830KB)

約20時間走った後

649.6MiB / 768MiB     84.59%               

[ec2-user@ip-10-180-28-222 ~]$ docker exec 34d7 jcmd 1 VM.native_memory summary
1:

Native Memory Tracking:

Total: reserved=1741020KB, committed=510928KB
-                 Java Heap (reserved=262144KB, committed=262144KB)
                            (mmap: reserved=262144KB, committed=262144KB)

-                     Class (reserved=1138319KB, committed=100495KB)
                            (classes #16390)
                            (malloc=7823KB #30851)
                            (mmap: reserved=1130496KB, committed=92672KB)

-                    Thread (reserved=30996KB, committed=30996KB)
                            (thread #31)
                            (stack: reserved=30800KB, committed=30800KB)
                            (malloc=97KB #156)
                            (arena=99KB #57)

-                      Code (reserved=261330KB, committed=69062KB)
                            (malloc=11730KB #16047)
                            (mmap: reserved=249600KB, committed=57332KB)

-                        GC (reserved=15363KB, committed=15363KB)
                            (malloc=5779KB #334)
                            (mmap: reserved=9584KB, committed=9584KB)

-                  Compiler (reserved=223KB, committed=223KB)
                            (malloc=92KB #1246)
                            (arena=131KB #6)

-                  Internal (reserved=8358KB, committed=8358KB)
                            (malloc=8326KB #18561)
                            (mmap: reserved=32KB, committed=32KB)

-                    Symbol (reserved=20253KB, committed=20253KB)
                            (malloc=17527KB #177997)
                            (arena=2725KB #1)

-    Native Memory Tracking (reserved=3846KB, committed=3846KB)
                            (malloc=10KB #127)
                            (tracking overhead=3836KB)

-               Arena Chunk (reserved=188KB, committed=188KB)
                            (malloc=188KB)

観察

これまでに20時間の観察後にわかったこと:

  • Docker統計が492.8MiBから649.6MiBに急上昇しました
  • JVMがコミットしたネイティブメモリが472227KBから510928KBにジャンプしました
  • Docker statsとJVMによってコミットされたネイティブメモリのギャップが拡大しているようです。 (649.6MiB - 510928KBはどこにあり、なぜ成長しているのですか)
  • この期間中、JVM統計は通常のままです。

したがって、JVM側で他に何をデバッグできるかわかりません。 Javaはヒープ(したがってネイティブメモリトラッキング)だけでなく)を必要とすることを理解していますが、jvmネイティブメモリトラッカーが報告するものとDocker統計が表示するものとの間に約150mのギャップがあります。その記憶がどこに向かっているのか、もっと洞察を得るにはどうすればよいですか?

16
ddewaele

JVMはメモリがすべてではないことを報告しました。

JVMのメモリフットプリント

では、JVMのメモリフットプリントに影響するものは何でしょうか。 Javaアプリケーションを実行しているほとんどの人は、最大ヒープ領域を設定する方法を知っています。しかし、実際には、メモリフットプリントに多くの原因があります。

  • ネイティブJRE
  • パーマ/メタスペース
  • JITバイトコード
  • JNI
  • NIO
  • スレッド

これは、Dockerコンテナでメモリ制限を設定する場合に留意する必要がある多くのことです。また、コンテナのメモリ制限を最大ヒープ領域に設定しても、十分ではない可能性があります…

JVMとCPU

JVMが実行されているノードで利用可能なプロセッサ/コアの数にJVMがどのように適応するかを簡単に見てみましょう。実際には多くのパラメータがあり、デフォルトではコア数に基づいて初期化されます。

  • J JITコンパイラスレッド
  • ♯ガベージコレクションのスレッド
  • 一般的なfork-joinプールのスレッドの…

したがって、JVMが32コアノードで実行されている場合(1つがデフォルトを上書きしなかった場合)、JVMは32のガベージコレクションスレッド、32のJITコンパイラスレッドなどを生成します。 ソース

この問題を回避するには、 +UseContainerSupport (8u191がデフォルトで有効になっているため、Java 10)で使用可能)、おそらく-XX:MaxRAMPercentage=90.0以下は、観察された総使用メモリに応じて異なります。詳細は this および that を参照してください。

私は強くお勧めします: "Nobody puts Java in the container:Ken Sipe" from JavaZone

1
janisz

A. janiszの回答を注意深く読み、リンクをたどってください。これは、Javaをコンテナーまたはcgroupsの下で使用しているユーザーにとって非常に重要な情報です。

B.主な問題は、JVMがコンテナのメモリ制限を認識しないことです。それは、ホストOSの空きメモリ全体を操作できると信じています。 cgroupの制限が許可するよりも多くのメモリを消費しようとすると、カーネル/ドッカーはcgroupのメモリ制限の約束に違反したためにコンテナを強制終了します。これは何 -XX:+UseContainerSupportおよび古い-XX:+UseCGroupMemoryLimitForHeapフラグは修正されることになっています。JVMに真の制限を知らせてください。

追加情報

-Xmxフラグは、JVM(Linuxプロセスとして)がオペレーティングシステムから必要とするすべてのメモリを制限しません。 Javaスタック、メタスペース、ロードされたコードなど(janiszの回答で説明)を含む)JVM自体のすべての機械化は、メモリスペースも使用します。

残念ながら、JVMは必要に応じてオペレーティングシステムからできるだけ多くのメモリを取得することを好み、より多くのメモリ(使用可能と思われる場合)を取得してから、既存の(場合によっては解放可能な)メモリを再利用します。この動作の改善(つまり、JVMがシステム内の唯一のアクターであると想定しない)は、Java 12、に同梱されている新しい G1ガベージコレクター の一部として計画されています。オペレーティングシステムの唯一の目的は、JVMが実行しているこの1つのJVMにサービスを提供することであると想定すると、JVMのメモリ使用量は常に増加し、利用可能な空きメモリをすべて消費することになります。

0
Guss