ネイティブ・メモリー・トラッキング(NMT)は、Java HotSpot VMの内部メモリー使用状況を追跡するJava HotSpot VM機能です。NMTのスコープ、NMTを有効にする方法、およびその他の使用の詳細は、ネイティブ・メモリー・トラッキングを参照してください。
NMTでは、非JVMコードによるメモリーの割当てを追跡しないので、ネイティブ・コードのメモリー・リークを検出するには、オペレーティング・システムでサポートされたツールの使用が必要になる場合があります。
次の項では、VM内部メモリー割当てのモニターおよびVMメモリー・リークの診断方法について説明します。
メモリー・リークを検出するには、次の手順を実行します。
次のコマンドライン・オプションを使用して、サマリーまたは詳細なトラッキングが指定されたJVMを起動します: -XX:NativeMemoryTracking=summary
または-XX:NativeMemoryTracking=detail
初期のベースラインの確立 - NMTベースライン機能を使用して、開発とメンテナンスのときに比較するベースラインを取得するには、次を実行します: jcmd <pid> VM.native_memory baseline
次を使用して、メモリーの変更をモニターします: jcmd <pid> VM.native_memory detail.diff
アプリケーションのメモリー・リークが少量の場合、それが明らかになるまで時間がかかる場合があります。
ネイティブ・メモリー・トラッキングをjcmdユーティリティとともに使用して、メモリーをモニターし、開発またはメンテナンス時にアプリケーションのメモリー使用量の増加が開始されないように設定できます。NMTのメモリー・カテゴリの詳細は、表2-1を参照してください。
次の項では、NMTのサマリーまたは詳細データを取得する方法、およびサンプル出力の解釈の方法を説明します。
サンプル出力の解釈: 以下のサンプル出力から、予約済メモリーとコミット済メモリーを確認できます。コミット済メモリーのみが実際に使用されることに注意してください。たとえば、-Xms100m -Xmx1000m
で実行した場合、JVMでは1000MBがJavaヒープ用に予約されます。初期のヒープ・サイズは100MBのみであるため、最初に100MBのみがコミットされます。アドレス領域がほぼ無制限である64ビット・マシンの場合、JVMで多くのメモリーが予約されても問題はありません。コミットされるメモリーが次第に増加し、スワッピングやネイティブOOM状態になる可能性がある場合に、問題が発生します。
Arenaは、mallocを使用して割り当てられるメモリー・チャンクです。スコープを終了またはコード領域を離れるとき、これらのチャンクからメモリーがバルクで解放されます。これらのチャンクは、一時メモリーを保持するために他のサブシステムで再利用できます(プリスレッドの割当てなど)。Arenaのmallocポリシーは、メモリー・リークがないことを確認します。したがって、個々のオブジェクトではなく、Arena全体が追跡されます。初期のメモリー量の一部は追跡できません。
NMTを有効にすると、JVMパフォーマンスが5-10%低下し、NMTのメモリー使用量により、すべてのmallocメモリーに2つのマシン・ワードがmallocヘッダーとして追加されます。NMTのメモリー使用量も、NMTにより追跡されます。
サマリー・データの取得: ネイティブ・メモリー使用量のサマリー・ビューを取得するには、次のコマンドライン・オプションを使用してJVMを起動します: -XX:NativeMemoryTracking=summary
例2-2は、追跡レベルがサマリーに設定されたNMTを示すサンプル出力です。このサンプル出力を得るための1つの方法は、jcmd <pid> VM.native_memory summary
を実行することです。
例2-2 NMTのサマリー・データの取得
Total: reserved=664192KB, committed=253120KB <--- total memory tracked by Native Memory Tracking - Java Heap (reserved=516096KB, committed=204800KB) <--- Java Heap (mmap: reserved=516096KB, committed=204800KB) - Class (reserved=6568KB, committed=4140KB) <--- class metadata (classes #665) <--- number of loaded classes (malloc=424KB, #1000) <--- malloc'd memory, #number of malloc (mmap: reserved=6144KB, committed=3716KB) - Thread (reserved=6868KB, committed=6868KB) (thread #15) <--- number of threads (stack: reserved=6780KB, committed=6780KB) <--- memory used by thread stacks (malloc=27KB, #66) (arena=61KB, #30) <--- resource and handle areas - Code (reserved=102414KB, committed=6314KB) (malloc=2574KB, #74316) (mmap: reserved=99840KB, committed=3740KB) - GC (reserved=26154KB, committed=24938KB) (malloc=486KB, #110) (mmap: reserved=25668KB, committed=24452KB) - Compiler (reserved=106KB, committed=106KB) (malloc=7KB, #90) (arena=99KB, #3) - Internal (reserved=586KB, committed=554KB) (malloc=554KB, #1677) (mmap: reserved=32KB, committed=0KB) - Symbol (reserved=906KB, committed=906KB) (malloc=514KB, #2736) (arena=392KB, #1) - Memory Tracking (reserved=3184KB, committed=3184KB) (malloc=3184KB, #300) - Pooled Free Chunks (reserved=1276KB, committed=1276KB) (malloc=1276KB) - Unknown (reserved=33KB, committed=33KB) (arena=33KB, #1)
詳細データの取得: ネイティブ・メモリー使用量の詳細ビューを取得するには、次のコマンドライン・オプションを使用してJVMを起動します: -XX:NativeMemoryTracking=detail
これは、最も多くのメモリーを割り当てるメソッドを正確に追跡します。NMTを有効にすると、JVMパフォーマンスが5-10%低下し、NMTのメモリー使用量により、すべてのmallocメモリーに2ワードがmallocヘッダーとして追加されます。NMTのメモリー使用量も、NMTにより追跡されます。
例2-3は、追跡レベルが詳細に設定された仮想メモリーのサンプル出力を示しています。このサンプル出力を得るための1つの方法は、jcmd <pid> VM.native_memory detail
を実行することです。
例2-3 NMTの詳細データの取得
Virtual memory map: [0x8f1c1000 - 0x8f467000] reserved 2712KB for Thread Stack from [Thread::record_stack_base_and_size()+0xca] [0x8f1c1000 - 0x8f467000] committed 2712KB from [Thread::record_stack_base_and_size()+0xca] [0x8f585000 - 0x8f729000] reserved 1680KB for Thread Stack from [Thread::record_stack_base_and_size()+0xca] [0x8f585000 - 0x8f729000] committed 1680KB from [Thread::record_stack_base_and_size()+0xca] [0x8f930000 - 0x90100000] reserved 8000KB for GC from [ReservedSpace::initialize(unsigned int, unsigned int, bool, char*, unsigned int, bool)+0x555] [0x8f930000 - 0x90100000] committed 8000KB from [PSVirtualSpace::expand_by(unsigned int)+0x95] [0x902dd000 - 0x9127d000] reserved 16000KB for GC from [ReservedSpace::initialize(unsigned int, unsigned int, bool, char*, unsigned int, bool)+0x555] [0x902dd000 - 0x9127d000] committed 16000KB from [os::pd_commit_memory(char*, unsigned int, unsigned int, bool)+0x36] [0x9127d000 - 0x91400000] reserved 1548KB for Thread Stack from [Thread::record_stack_base_and_size()+0xca] [0x9127d000 - 0x91400000] committed 1548KB from [Thread::record_stack_base_and_size()+0xca] [0x91400000 - 0xb0c00000] reserved 516096KB for Java Heap <--- reserved memory range from [ReservedSpace::initialize(unsigned int, unsigned int, bool, char*, unsigned int, bool)+0x190] <--- callsite that reserves the memory [0x91400000 - 0x93400000] committed 32768KB from [VirtualSpace::initialize(ReservedSpace, unsigned int)+0x3e8] <--- committed memory range and its callsite [0xa6400000 - 0xb0c00000] committed 172032KB from [PSVirtualSpace::expand_by(unsigned int)+0x95] <--- committed memory range and its callsite [0xb0c61000 - 0xb0ce2000] reserved 516KB for Thread Stack from [Thread::record_stack_base_and_size()+0xca] [0xb0c61000 - 0xb0ce2000] committed 516KB from [Thread::record_stack_base_and_size()+0xca] [0xb0ce2000 - 0xb0e83000] reserved 1668KB for GC from [ReservedSpace::initialize(unsigned int, unsigned int, bool, char*, unsigned int, bool)+0x555] [0xb0ce2000 - 0xb0cf0000] committed 56KB from [PSVirtualSpace::expand_by(unsigned int)+0x95] [0xb0d88000 - 0xb0d96000] committed 56KB from [CardTableModRefBS::resize_covered_region(MemRegion)+0xebf] [0xb0e2e000 - 0xb0e83000] committed 340KB from [CardTableModRefBS::resize_covered_region(MemRegion)+0xebf] [0xb0e83000 - 0xb7003000] reserved 99840KB for Code from [ReservedSpace::initialize(unsigned int, unsigned int, bool, char*, unsigned int, bool)+0x555] [0xb0e83000 - 0xb0e92000] committed 60KB from [VirtualSpace::initialize(ReservedSpace, unsigned int)+0x3e8] [0xb1003000 - 0xb139b000] committed 3680KB from [VirtualSpace::initialize(ReservedSpace, unsigned int)+0x37a] [0xb7003000 - 0xb7603000] reserved 6144KB for Class from [ReservedSpace::initialize(unsigned int, unsigned int, bool, char*, unsigned int, bool)+0x555] [0xb7003000 - 0xb73a4000] committed 3716KB from [VirtualSpace::initialize(ReservedSpace, unsigned int)+0x37a] [0xb7603000 - 0xb760b000] reserved 32KB for Internal from [PerfMemory::create_memory_region(unsigned int)+0x8ba] [0xb770b000 - 0xb775c000] reserved 324KB for Thread Stack from [Thread::record_stack_base_and_size()+0xca] [0xb770b000 - 0xb775c000] committed 324KB from [Thread::record_stack_base_and_size()+0xca]
NMTベースラインからの差分の取得: サマリーおよび詳細レベルの追跡では、アプリケーションが起動され実行されているとき、ベースラインを設定できます。これを行うには、アプリケーションのウォーム・アップ後にjcmd <pid> VM.native_memory baseline
を実行します。次に、jcmd <pid> VM.native_memory summary.diff
またはjcmd <pid> VM.native_memory detail.diff
を実行できます。
例2-4は、ベースライン以降に使用されたネイティブ・メモリーの差分サマリーのサンプル出力を示し、これはメモリー・リークを見つけるのに優れた方法です。
例2-4 NMTベースラインからの差分の取得
Total: reserved=664624KB -20610KB, committed=254344KB -20610KB <--- total memory changes vs. earlier baseline. '+'=increase '-'=decrease - Java Heap (reserved=516096KB, committed=204800KB) (mmap: reserved=516096KB, committed=204800KB) - Class (reserved=6578KB +3KB, committed=4530KB +3KB) (classes #668 +3) <--- 3 more classes loaded (malloc=434KB +3KB, #930 -7) <--- malloc'd memory increased by 3KB, but number of malloc count decreased by 7 (mmap: reserved=6144KB, committed=4096KB) - Thread (reserved=60KB -1129KB, committed=60KB -1129KB) (thread #16 +1) <--- one more thread (stack: reserved=7104KB +324KB, committed=7104KB +324KB) (malloc=29KB +2KB, #70 +4) (arena=31KB -1131KB, #32 +2) <--- 2 more arenas (one more resource area and one more handle area) - Code (reserved=102328KB +133KB, committed=6640KB +133KB) (malloc=2488KB +133KB, #72694 +4287) (mmap: reserved=99840KB, committed=4152KB) - GC (reserved=26154KB, committed=24938KB) (malloc=486KB, #110) (mmap: reserved=25668KB, committed=24452KB) - Compiler (reserved=106KB, committed=106KB) (malloc=7KB, #93) (arena=99KB, #3) - Internal (reserved=590KB +35KB, committed=558KB +35KB) (malloc=558KB +35KB, #1699 +20) (mmap: reserved=32KB, committed=0KB) - Symbol (reserved=911KB +5KB, committed=911KB +5KB) (malloc=519KB +5KB, #2921 +180) (arena=392KB, #1) - Memory Tracking (reserved=2073KB -887KB, committed=2073KB -887KB) (malloc=2073KB -887KB, #84 -210) - Pooled Free Chunks (reserved=2624KB -15876KB, committed=2624KB -15876KB) (malloc=2624KB -15876KB)
例2-5は、ベースライン以降に使用されたネイティブ・メモリーの差分詳細を示すサンプル出力で、これはメモリー・リークを見つけるのに優れた方法です。
例2-5 NMTの差分詳細の取得
Details: [0x01195652] ChunkPool::allocate(unsigned int)+0xe2 (malloc=482KB -481KB, #8 -8) [0x01195652] ChunkPool::allocate(unsigned int)+0xe2 (malloc=2786KB -19742KB, #134 -618) [0x013bd432] CodeBlob::set_oop_maps(OopMapSet*)+0xa2 (malloc=591KB +6KB, #681 +37) [0x013c12b1] CodeBuffer::block_comment(int, char const*)+0x21 <--- [callsite address] method name + offset (malloc=562KB +33KB, #35940 +2125) <--- malloc'd amount, increased by 33KB #malloc count, increased by 2125 [0x0145f172] ConstantPool::ConstantPool(Array<unsigned char>*)+0x62 (malloc=69KB +2KB, #610 +15) ... [0x01aa3ee2] Thread::allocate(unsigned int, bool, unsigned short)+0x122 (malloc=21KB +2KB, #13 +1) [0x01aa73ca] Thread::record_stack_base_and_size()+0xca (mmap: reserved=7104KB +324KB, committed=7104KB +324KB)