こんにちは、Windows サポートの新川です。
ゲスト OS のメモリ使用状況について、以下のご質問をいただく事があります。
これらは、そのゲスト OS のメモリ管理を “動的” に設定されているのであれば、バルーニング と呼ばれる仕組みによって発生しているものが大半を占めます。今回は、バルーニングの見え方について少しお話しします。
■ Dynamic Memoryの実装
まずはバルーニングの前に Dynamic Memory についての説明です。Windows Server 2008 R2 SP1 より、Hot Add Memory が可能なゲスト OS に対して、Hyper-V ホストからシステム稼働中に動的に物理メモリを割り当てる事が可能になりました。この機能により、ゲスト OS に割り当てられるメモリが動的に最適化出来るようになっています。ただし、Dynamic Memory のゲスト OS の要件には、Hot Add Memory というのはありますが、Hot Remove Memoryというのはありません。つまり、ゲスト OS は “動的に物理メモリを追加” さえサポートしていれば、”動的に物理メモリを削除” する事をサポートされていなくても、Hyper-V ホストはゲスト OS から使っていない物理メモリを回収しているという事になります。(Hyper-V Dynamic Memoryメモリの要求要件についての詳細は 、Hyper-V Dynamic Memory Configuration Guide をご参照ください。)
■ Hyper-V ホストはどのようにしてゲスト OS から物理メモリを回収するか。
それでは、Hyper-V ホストはどのようにゲスト OS から物理メモリを回収しているのでしょうか。実は、Dynamic Memory については既に詳細について記載されたホワイトペーパーが公開されており、Dynamic Memory Technical Overview whitepaper (英語) と Dynamic Memory の実装と構成 (日本語) – ベータ版 からダウンロード可能です。他にも Tech・Ed Japan 2010 の Effective Hyper-V R2 SP1 ~ 詳説 Dynamic Memory ~ には、Hyper-V 以外の製品との比較も含めて非常に細かく書かれています。簡単に説明すると、SP1 で新しくなった Hyper-V ホストの VSP (Virtual Service Provider) とゲスト OS の VSC (Virtual Service Client) で、ゲスト OS の物理メモリのニーズの判断や割り当てを行っています。
ゲスト OS から物理メモリの回収は、ゲスト OS 上で物理メモリを減らすのではなく、特定のメモリ ページにゲスト OS からアクセス出来ない状態にする事で実現します。この領域は、再びゲスト OS の物理メモリ使用量があがって必要になった場合は小さくなり、そのゲスト OS で利用可能な物理メモリ量が増えます。反対に、ゲスト OS で必要な物理メモリ量が減れば、このページは大きくなります。風船のように膨らんだり萎んだりする事から、この仕組がバルーニングと呼ばれています。
■ バルーニングの様子は、メモリ監視をしているとどのように見えるか。
それでは、動的メモリを意識せず、ゲスト OS 上のみでメモリ使用状況を監視していた場合、どのように見えるか確認してみましょう。この環境では、Windows の限界に挑むシリーズのMark’s ブログ でも利用されている testlimit ツール (こちらからダウンロード可能です)にて、大量にメモリを予約するテストを行った後、プロセスが停止してしばらく時間が経過しています。Hyper-V コンソールで確認すると、現在は 768MB が割り当てられています。
それでは、ゲスト OS 内の状況を確認してみましょう。まずは、ゲスト OS のタスクマネージャーを見てみます。以下のように搭載物理メモリが 6,981 MBで、うち 6.57GB (6,727 MB) が利用中となっています。
上記から、カーネル メモリの使用量が非常に少ないことは見えますが、プロセス タブを開いても、使用量の多いプロセスは見つかりません。
次に、ゲスト OS 上のパフォーマンス モニタです。Available Bytes が大きく増減していますが、物理メモリの内訳になりそうな、Pool NonPaged Bytes や Cache Bytes (= System Cache Resident Bytes + System Driver Resident Bytes + System Code Resident Bytes + Pool Paged Resident Bytes のカウンターの合計) や、Process\Working Set で全プロセスの合計を見ても、同じ単位での動きがありません。Committed Bytes が大きく上がっている事は見えますが、これだけでは何に使っているのかがわかりません。
更に、ゲスト OS 内で RAMMap ツールを用いるともう少し内訳が見え、Driver Locked が 6,229,244 KB (≒ 6083 MB ≒ 5.94 GB) を占めている事がわかります。しかし、これ以上の詳細は掴むことが出来ません。
(なお、RAMMap を動かした際にこのゲスト OS への物理メモリの割り当てが少し増えてしまったため、バルーニングのサイズは小さくなっています)
■ 動的メモリ割り当て状況を確認する方法
上述の通り、ゲスト OS 内だけでメモリ使用状況を確認しても、実際にゲスト OS 内でメモリ使用量があがって枯渇しかけているのか、バルーニングの動作で使用量が上がっているように見えるのか、判断が付きませんでした。これについてはHyper-V ホストで以下のカウンタなどが追加されているため、このカウンタから確認が可能です。
Hyper-V Dynamic Memory VM カウンタの中で、Hyper-V Dynamic Memory VM\Physical Memory というインスタンスが実際にゲスト OS に割り当てられているメモリサイズです。今回の例では、一時的に 6,982 MB (≒ 6.8GB) が割り当てられ、その後すぐに割り当てが 826 MB まで減っている事が確認出来ます。今後、物理メモリの利用状況を監視されている環境で動的メモリをご利用になる場合は、Hyper-V ホストのパフォーマンス ログも同時に取得ください。
■ おまけ。ゲスト OS のメモリ ダンプなどからバルーニングを認識出来るか?
ゲスト OS のメモリ ダンプしかない状況で、バルーニングによって利用されている事が特定出来るか確認してみます。
物理メモリの利用状況の確認には !memusage コマンドが用いられますが、以下のように AWE で 6,273,624 KB (≒ 6127 MB ≒ 5.98 GB ) 使用中となっています。
kd> !memusageloading PFN databaseloading (100% complete)Compiling memory usage data (99% Complete). Zeroed: 48307 (193228 kb) Free: 5 ( 20 kb) Standby: 19724 ( 78896 kb) Modified: 821 ( 3284 kb) ModifiedNoWrite: 0 ( 0 kb) Active/Valid: 1713940 (6855760 kb) Transition: 2 ( 8 kb) Bad: 383 ( 1532 kb) Unknown: 0 ( 0 kb) TOTAL: 1782799 (7131196 kb) Building kernel map Finished building kernel mapScanning PFN database - (100% complete)
Usage Summary (in Kb):Control Valid Standby Dirty Shared Locked PageTables nameffffffffffffd 6273624 0 0 0 0 0 AWEfffffa80308a69c0 1056 160 0 956 0 0 mapped_file( ntdll.dll )fffffa8031297690 4988 6080 0 0 0 0 mapped_file( System.ServiceModel.ni.dll )fffffa8030884ce0 100 696 4 0 0 0 mapped_file( $BitMap )fffffa80308a5e30 48 636 0 0 0 0 mapped_file( ntdll.dll )fffffa80327979d0 76 96 0 0 0 0 mapped_file( werconcpl.dll )fffffa8030881930 64 1896 0 0 0 0 mapped_file( $LogFile )fffffa80312d98b0 108 636 0 0 0 0 mapped_file( ntfrs.exe )fffffa803088ba50 4 0 0 0 0 0 No Name for File:-------- 0 0 0 ----- 0 ----- driver ( RDPDD.dll )-------- 436 16 0 ----- 0 ----- driver ( spsys.sys )-------- 56 0 0 ----- 0 ----- driver ( crashdmp.sys )-------- 48 0 0 ----- 0 ----- driver ( dump_ataport.sys )-------- 36 0 0 ----- 0 ----- driver ( dump_atapi.sys )-------- 67840 4 0 ----- 1048 160 ( Paged Pool )-------- 5820 156 1512 ----- 0 140 ( Kernel Stacks )-------- 42540 0 0 ----- 0 160 ( NonPaged Pool )-------- 188 0 0 ----- 0 24 ( System Working Set Structures )Summary 6855760 78904 3284 40764 75444 13432 Total
しかし、念のため AWE について確認しても、明示的に AWE を使っているプロセスはありません。
kd> !for_each_process "dt _eprocess @#Process AweInfo"nt!_EPROCESS +0x388 AweInfo : (null) nt!_EPROCESS +0x388 AweInfo : (null) nt!_EPROCESS +0x388 AweInfo : (null) nt!_EPROCESS +0x388 AweInfo : (null) nt!_EPROCESS +0x388 AweInfo : (null) nt!_EPROCESS +0x388 AweInfo : (null) nt!_EPROCESS +0x388 AweInfo : (null) nt!_EPROCESS +0x388 AweInfo : (null) nt!_EPROCESS +0x388 AweInfo : (null) nt!_EPROCESS +0x388 AweInfo : (null):nt!_EPROCESS +0x388 AweInfo : (null) nt!_EPROCESS +0x388 AweInfo : (null) nt!_EPROCESS +0x388 AweInfo : (null) nt!_EPROCESS +0x388 AweInfo : (null) nt!_EPROCESS +0x388 AweInfo : (null) nt!_EPROCESS +0x388 AweInfo : (null) nt!_EPROCESS +0x388 AweInfo : (null) nt!_EPROCESS +0x388 AweInfo : (null)
実は、!memusage コマンドで AWE フレームと認識されている領域には MDL で確保された領域が含まれるため、MmAllocatePagesForMdl などで確保された場合にもこのような結果になります。(RAMMap ではその点を正しく認識して表示しているので、AWE ではなく “Driver Locked” と表示されます)
残念ながら MDL 領域は、Pool のようにどのドライバから要求されたものかを確認する方法がないので、メモリダンプでも特定は困難です。下記のように MDL を使う可能性があるドライバを探しても複数あり、ここから黄色マークの dmsvc.sys によって確保された事を裏付ける事が出来ません。
kd> x nt!MmAllocatePagesForMdl*fffff800`015ef9a0 nt!MmAllocatePagesForMdl = <no type information>fffff800`015ef900 nt!MmAllocatePagesForMdlEx = <no type information>
// ドライバ内に nt!MmAllocatePagesForMdl のアドレス (fffff800`015ef9a0) が含まれており、MmAllocatePagesForMdl を使っている可能性があるもの (抜粋)
kd> !for_each_module ".echo ${@#ModuleName} ;s -q ${@#ModuleName} L?${@#Size} fffff800`015ef9a0"halfffff800`0142b098 fffff800`015ef9a0 fffff800`01473f04ntfffff800`0199e928 fffff800`015ef9a0 fffff800`0199c8a8CIfffff880`011ed0c0 fffff800`015ef9a0 fffff800`0155d420NDISfffff880`011ed0c0 fffff800`015ef9a0 fffff800`0155d420msrpcfffff880`011ed0c0 fffff800`015ef9a0 fffff800`0155d420ACPIfffff880`011ed0c0 fffff800`015ef9a0 fffff800`0155d420pcifffff880`011ed0c0 fffff800`015ef9a0 fffff800`0155d420Wdf01000fffff880`011ed0c0 fffff800`015ef9a0 fffff800`0155d420WDFLDRfffff880`011ed0c0 fffff800`015ef9a0 fffff800`0155d420vmbusfffff880`011ed0c0 fffff800`015ef9a0 fffff800`0155d420winhvfffff880`011ed0c0 fffff800`015ef9a0 fffff800`0155d420Ntfsfffff880`01992470 fffff800`015ef9a0 fffff800`018e2780tcpipfffff880`01992470 fffff800`015ef9a0 fffff800`018e2780fwpkclntfffff880`01992470 fffff800`015ef9a0 fffff800`018e2780volsnapfffff880`01992470 fffff800`015ef9a0 fffff800`018e2780CLASSPNPfffff880`01992470 fffff800`015ef9a0 fffff800`018e2780cdromfffff880`01992470 fffff800`015ef9a0 fffff800`018e2780dfsfffff880`01992470 fffff800`015ef9a0 fffff800`018e2780Nullfffff880`01992470 fffff800`015ef9a0 fffff800`018e2780vgafffff880`01992470 fffff800`015ef9a0 fffff800`018e2780VIDEOPRTfffff880`01992470 fffff800`015ef9a0 fffff800`018e2780rdbssfffff880`02858470 fffff800`015ef9a0 fffff800`015c4d40netbtfffff880`02858470 fffff800`015ef9a0 fffff800`015c4d40pacerfffff880`02858470 fffff800`015ef9a0 fffff800`015c4d40serialfffff880`02858470 fffff800`015ef9a0 fffff800`015c4d40blbdrivefffff880`02858470 fffff800`015ef9a0 fffff800`015c4d40raspptpfffff880`02858470 fffff800`015ef9a0 fffff800`015c4d40rassstpfffff880`02858470 fffff800`015ef9a0 fffff800`015c4d40rdpbusfffff880`02858470 fffff800`015ef9a0 fffff800`015c4d40ksfffff880`02858470 fffff800`015ef9a0 fffff800`015c4d40
// 同じく nt!MmAllocatePagesForMdlEx (fffff800`015ef900) を呼び出す可能性があるドライバ抜粋
kd> !for_each_module ".echo ${@#ModuleName} ;s -q ${@#ModuleName} L?${@#Size} fffff800`015ef900"ntfffff800`0199e940 fffff800`015ef900 fffff800`0199c1d0dmvscfffff880`00c17188 fffff800`015ef900 fffff800`014d09c0CLFSfffff880`00e41070 fffff800`015ef900 fffff800`014b3550CIfffff880`00e41070 fffff800`015ef900 fffff800`014b3550fffff880`011f13e8 fffff800`015ef900 00000000`00000000HIDCLASSfffff880`00e41070 fffff800`015ef900 fffff800`014b3550volmgrxfffff880`00e41070 fffff800`015ef900 fffff800`014b3550amdxatafffff880`00e41070 fffff800`015ef900 fffff800`014b3550NETIOfffff880`00e41070 fffff800`015ef900 fffff800`014b3550NDISfffff880`011f13e8 fffff800`015ef900 00000000`00000000msrpcfffff880`011f13e8 fffff800`015ef900 00000000`00000000ACPIfffff880`011f13e8 fffff800`015ef900 00000000`00000000pcifffff880`011f13e8 fffff800`015ef900 00000000`00000000Wdf01000fffff880`011f13e8 fffff800`015ef900 00000000`00000000WDFLDRfffff880`011f13e8 fffff800`015ef900 00000000`00000000vmbusfffff880`011f13e8 fffff800`015ef900 00000000`00000000winhvfffff880`011f13e8 fffff800`015ef900 00000000`00000000
// バルーニングを行なうドライバは、上記では黄色マークがついている以下の dmvsc.sys となります。しかし、メモリ ダンプから今回の MDL 領域が dmvsc.sys によって確保された事を裏付ける情報がありません。
Module[3006] [C:\WINDOWS\SYSTEM32\DRIVERS\DMVSC.SYS] Company Name: Microsoft Corporation File Description: Dynamic Memory Product Version: 6.1.7601.17514 File Version: 6.1.7601.17514 (win7sp1_rtm.101119-1850) File Size (bytes): 71168 File Date: ? 11 21 12:24:00 2010 Module TimeDateStamp = 0x4ce79b97 - Sat Nov 20 18:57:43 2010 Module Checksum = 0x000120f5 Module SizeOfImage = 0x00018000 Module Pointer to PDB = [dmvsc.pdb] Module PDB Guid = {1A806C15-8753-40A0-82DB-9868074FDFDC} Module PDB Age = 0x1