中程度の負荷の下でJavaプロセスのメモリ使用量を調査しようとしています。
PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
12663 test 20 0 8378m 6.0g 4492 S 43 8.4 162:29.95 Java
ご覧のとおり、6Gbに常駐メモリがあります。興味深い部分はこれです:プロセスはこれらのパラメーターで実行されます:
これらの設定と実際のメモリ使用量を見ると、このプロセスが使用しているものと実際に使用しているものの違いに戸惑うことがあります。
通常、メモリの問題はヒープダンプを分析することで解決されますが、この場合、メモリはヒープ以外の場所で使用されます。
質問:このような高いメモリ使用量の理由を試し、見つけるための手順は何ですか?そのプロセスでメモリを使用しているものを特定するのに役立つツールはどれですか。
編集
まだかなりのスペースがあるため、これはヒープ関連の問題ではないようです。
jmap -heap 12663
結果(スペース節約のために編集)
Heap Configuration:
MinHeapFreeRatio = 40
MaxHeapFreeRatio = 70
MaxHeapSize = 2147483648 (2048.0MB)
NewSize = 536870912 (512.0MB)
MaxNewSize = 536870912 (512.0MB)
OldSize = 1610612736 (1536.0MB)
NewRatio = 7
SurvivorRatio = 8
PermSize = 21757952 (20.75MB)
MaxPermSize = 85983232 (82.0MB)
New Generation: 45.7% used
Eden Space: 46.3% used
From Space: 41.4% used
To Space: 0.0% used
concurrent mark-sweep generation: 63.7% used
Perm Generation: 82.5% used
編集1
pmapを使用すると、64Mbのかなりの量の割り当てがあることがわかります。
pmap -x 12663 | grep rwx | sort -n -k3 | less
結果は:
... a lot more of these 64Mb chunks
00007f32b8000000 0 65508 65508 rwx-- [ anon ] <- what are these?
00007f32ac000000 0 65512 65512 rwx-- [ anon ]
00007f3268000000 0 65516 65516 rwx-- [ anon ]
00007f3324000000 0 65516 65516 rwx-- [ anon ]
00007f32c0000000 0 65520 65520 rwx-- [ anon ]
00007f3314000000 0 65528 65528 rwx-- [ anon ]
00000000401cf000 0 241904 240980 rwx-- [ anon ] <- Direct memory ?
000000077ae00000 0 2139688 2139048 rwx-- [ anon ] <- Heap ?
では、これらの64Mbチャンクを見つける方法は?それらは何を使用していますか?それらにはどのようなデータがありますか?
ありがとう
問題はこれに関連している可能性があります glibcの問題 。
基本的に、メモリを割り当てる複数のスレッドがある場合、glibcはロックの競合を回避するために、割り当てを行うために利用可能なアリーナの数をスケールアップします。アリーナは64Mbです。上限は、コアアリーナの数の8倍を作成することです。アリーナは、スレッドがすでにロックされているアリーナにアクセスしたときにオンデマンドで作成されるため、時間とともに成長します。
Javaでは、スレッドを振りかけると、すぐに多くのアリーナが作成される可能性があります。また、これらのアリーナ全体に割り当てが広がっています。最初は、各64Mbアリーナはコミットされていないメモリにマップされていますが、割り当てを行うと、実際のメモリを使用し始めます。
あなたのpmapはおそらく以下のようなリストを持っています。 324K + 65212K = 65536K、560K + 64976K == 65536K、620K + 64916K == 65536Kであることに注意してください。つまり、合計で64Mbになります。
00007f4394000000 324K rw --- [anon] 00007f4394051000 65212K ----- [anon] 00007f4398000000 560K rw --- [anon] 00007f439808c000 64976K ----- [anon] 00007f439c000000 620K rw --- [anon] 00007f439c09b000 64916K ----- [anon]
回避策の場合:バグでは、アリーナの数を制限するために設定できるいくつかの環境パラメータについて言及していますが、十分な高さのglibcバージョンが必要です。
Lambdaプローブ はどうですか?特に、以下のスクリーンショットのようなメモリ使用量の内訳を表示できます。
Sometimes pmap -x your_Java_pid
can also be helpful.
問題はヒープの外側にあるため、最適な候補は次のとおりです。
JNI leak
Allocation of direct memory buffer
ダイレクトバッファーのサイズを制限したという事実のため、私の意見では、JNIリークが最適です。
JProfilerはあなたが求めるものかもしれませんが、無料ではありません。 JavaプロセスはJava VisualVMはOracle/Sun JDKディストリビューションでJDKツールとして利用可能です。問題に対するより包括的なアプローチ(JDK + OS +ディスクなどを監視する)を推奨します– Nagios、Verax NMS、OpenNMSなどのネットワーク監視システムの使用。
Jmapと呼ばれるJDKに含まれるヒープメモリの割り当てを表示する便利なツールがあり、その上にスタックなど(Xss)もあります。次の2つのjmapコマンドを実行して、メモリ使用量に関する詳細情報を取得します。
jmap -heap <PID>
jmap -permstat <PID>
さらに多くの情報を取得するには、jconsole(JDKにも含まれています)を使用してプロセスに接続できます。ただし、Jconsoleでは、JMXをアプリケーションで構成する必要があります。
JVisualVMを使用します。 PermGenなど、使用されているヒープメモリの量を示すさまざまなビューがあります。
あなたの質問に答えることについて。 Javaは、予想とはかなり異なる方法でメモリを処理します。
-Xmsパラメーターと-Xmxパラメーターを設定すると、JVMにヒープ内で最初に割り当てる必要があるメモリの量と、最大値として割り当てる必要がある量もJVMに指示します。
Javaアプリケーションで合計1mのメモリを使用したが、-Xms256m -Xmx2gで渡された場合、JVMは256mのメモリを使用して初期化します。それ以下のメモリは使用しません。アプリケーションが1mのメモリしか使用しないことは問題ではありません。
第二に。上記の場合、ある時点でアプリが256mを超えるメモリを使用すると、JVMは要求を処理するために必要なだけのメモリを割り当てます。ただし、しないヒープサイズを最小値に戻します。少なくとも、ほとんどの状況ではそうではありません。
あなたのケースでは、最小メモリと最大メモリを2gに設定しているため、JVMは最初に2gを割り当て、それを維持します。
Javaのメモリ管理は非常に複雑であり、メモリ使用量の調整はそれ自体がタスクとなります。ただし、役立つリソースはたくさんあります。