Javaアプリ(YARN内)を実行し、ネイティブメモリトラッキングを有効にした場合(-XX:NativeMemoryTracking=detail
https://docs.Oracle.com/javase/8/docs/technotes/guides/vm/nmt-8.html および https://docs.Oracle。 com/javase/8/docs/technotes/guides/troubleshoot/tooldescr007.html )、さまざまなカテゴリでJVMが使用しているメモリの量を確認できます。
Jdk 1.8.0_45上の私のアプリは次を示します:
ネイティブメモリトラッキング: 合計:予約済み= 4023326KB、コミット済み= 2762382KB -Javaヒープ(予約済み= 1331200KB、コミット済み= 1331200KB) (mmap:予約済み= 1331200KB、コミット済み= 1331200KB) -クラス(予約済み= 1108143KB、コミット済み= 64559KB) (クラス#8621 ) (malloc = 6319KB#17371) (mmap:reserved = 1101824KB、commited = 58240KB) -スレッド(予約済み= 1190668KB、commited = 1190668KB) (スレッド#1154) (スタック:reserved = 1185284KB、commited = 1185284KB) (malloc = 3809KB#5771) (arena = 1575KB#2306) -コード(予約済み= 255744KB、コミット済み= 38384KB) (malloc = 614 4KB#8858) (mmap:reserved = 249600KB、commited = 32240KB) -GC(予約済み= 54995KB、committed = 54995KB) (malloc = 5775KB #217) (mmap:reserved = 49220KB、commited = 49220KB) -コンパイラー(予約済み= 267KB、committed = 267KB) (malloc = 137KB# 333) (arena = 131KB#3) -内部(予約済み= 65106KB、コミット済み= 65106KB) (malloc = 65074KB#29652) (mmap:reserved = 32KB、commited = 32KB) -シンボル(予約済み= 13622KB、コミット済み= 13622KB) (malloc = 12016KB#128199) (arena = 1606KB#1) -ネイティブメモリトラッキング(予約済み= 3361KB、コミット済み= 3361KB) (malloc = 287KB#3994) (追跡オーバーヘッド= 3075KB) -アリーナチャンク(予約済み= 220KB、コミット済み= 220KB) (malloc = 220KB)
これは、1.3GBの割り当てられたヒープとほぼ1.2GBの割り当てられたスレッドスタック(多くのスレッドを使用)を含む、2.7GBのコミットされたメモリを示しています。
ただし、ps ax -o pid,rss | grep <mypid>
またはtop
は1.6GBのRES/rss
常駐メモリ。スワップをチェックすると、何も使用されていません:
free -m キャッシュに使用された空き共有バッファーの合計 Mem:129180 99348 29831 0 2689 73024 -/ + buffers/cache:23633 105546 スワップ:15624 0 15624
1.6GBしか常駐していないのに、JVMが2.7GBのメモリがコミットされていると表示するのはなぜですか?残りはどこに行きましたか?
(JVMヒープとは異なり)スタックメモリが常駐することなく事前にコミットされているように見え、実際のスタック使用量の上限までしか常駐しなくなるのではないかと疑い始めています。
はい、少なくともLinuxでは、特に断りのない限りmmapは面倒です。ページは、書き込まれると物理メモリによってのみサポートされます( ゼロページ最適化 のため、読み取りは不十分です)。
GCヒープメモリは、コピーコレクターまたは事前ゼロ化(-XX:+AlwaysPreTouch
)によって効果的に影響を受けるため、常に常駐します。スレッドスタックはこれによる影響を受けません。
さらに確認するには、pmap -x <Java pid>
を使用して、さまざまなアドレス範囲のRSSをNMTの仮想メモリマップからの出力と相互参照できます。
予約メモリはPROT_NONE
でマッピングされています。つまり、仮想アドレス空間の範囲は、カーネルのvma構造体にエントリがあり、他のmmap/malloc呼び出しでは使用されません。ただし、SIGSEGVとしてプロセスに転送されるページフォールトは引き続き発生します。つまり、それらにアクセスするとエラーになります。
これは、連続したアドレス範囲を将来の使用に利用できるようにすることが重要です。これにより、ポインターの計算が簡単になります。
ストレージによってコミットされたが、バッキングされていないメモリは、たとえば[PROT_READ | PROT_WRITE
]でマップされていますが、それにアクセスすると、ページ違反が発生します。しかし、そのページフォールトは、実際のメモリでバックアップし、何も起こらなかったかのように実行に戻ることにより、カーネルによって静かに処理されます。
つまりプロセス自体には気づかれない実装の詳細/最適化です。
概念の内訳を示すには:
Used Heap:最後のGCによるライブオブジェクトによって占有されているメモリの量
Committed:PROT_NONE以外のものでマップされたアドレス範囲。遅延割り当てとページングが原因で、物理またはスワップによってサポートされる場合とされない場合があります。
Reserved:特定のメモリプールに対してmmap
を介して事前にマップされた合計アドレス範囲。
予約済み-コミット済みの違いは、物理メモリによってバックアップされないことが保証されているPROT_NONE
マッピングで構成されています
Resident:現在物理RAMにあるページ。これは、コード、スタック、コミットされたメモリプールの一部だけでなく、最近アクセスされたmmapされたファイルの一部と、JVMの制御外の割り当てを意味します。
Virtual:すべての仮想アドレスマッピングの合計。コミットされた予約済みのメモリプールだけでなく、マップされたファイルまたは共有メモリもカバーします。 JVMが非常に大きなアドレス範囲を事前に予約したり、大きなファイルをmmapしたりできるので、この数はほとんど情報を提供しません。