Share via


ゲスト OS の物理メモリが大量に使われている

こんにちは、Windows サポートの新川です。

 ゲスト OS のメモリ使用状況について、以下のご質問をいただく事があります。

  • タスクマネージャーでパフォーマンスを見ていたら、物理メモリが大量に使われているけれども、プロセス タブでそんなに物理メモリを使っているプロセスが見当たらない
  • パフォーマンス モニタを見ていると、突然 Memory\Available Mbytes が大きく減ったけど、その時間帯の Memory カウンタや Process カウンタを見ても、ほとんど物理メモリを使っているものが見つからない。

 これらは、そのゲスト 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  カウンタの中で、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> !memusage
loading PFN database
loading (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 map
Scanning PFN database - (100% complete)

  Usage Summary (in Kb):
Control Valid Standby Dirty Shared Locked PageTables name
ffffffffffffd 6273624 0 0 0 0 0 AWE
fffffa80308a69c0 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"
hal
fffff800`0142b098  fffff800`015ef9a0 fffff800`01473f04
nt
fffff800`0199e928  fffff800`015ef9a0 fffff800`0199c8a8
CI
fffff880`011ed0c0  fffff800`015ef9a0 fffff800`0155d420
NDIS
fffff880`011ed0c0  fffff800`015ef9a0 fffff800`0155d420
msrpc
fffff880`011ed0c0  fffff800`015ef9a0 fffff800`0155d420
ACPI
fffff880`011ed0c0  fffff800`015ef9a0 fffff800`0155d420
pci
fffff880`011ed0c0  fffff800`015ef9a0 fffff800`0155d420
Wdf01000
fffff880`011ed0c0  fffff800`015ef9a0 fffff800`0155d420
WDFLDR
fffff880`011ed0c0  fffff800`015ef9a0 fffff800`0155d420
vmbus
fffff880`011ed0c0  fffff800`015ef9a0 fffff800`0155d420
winhv
fffff880`011ed0c0  fffff800`015ef9a0 fffff800`0155d420
Ntfs
fffff880`01992470  fffff800`015ef9a0 fffff800`018e2780
tcpip
fffff880`01992470  fffff800`015ef9a0 fffff800`018e2780
fwpkclnt
fffff880`01992470  fffff800`015ef9a0 fffff800`018e2780
volsnap
fffff880`01992470  fffff800`015ef9a0 fffff800`018e2780
CLASSPNP
fffff880`01992470  fffff800`015ef9a0 fffff800`018e2780
cdrom
fffff880`01992470  fffff800`015ef9a0 fffff800`018e2780
dfs
fffff880`01992470  fffff800`015ef9a0 fffff800`018e2780
Null
fffff880`01992470  fffff800`015ef9a0 fffff800`018e2780
vga
fffff880`01992470  fffff800`015ef9a0 fffff800`018e2780
VIDEOPRT
fffff880`01992470  fffff800`015ef9a0 fffff800`018e2780
rdbss
fffff880`02858470  fffff800`015ef9a0 fffff800`015c4d40
netbt
fffff880`02858470  fffff800`015ef9a0 fffff800`015c4d40
pacer
fffff880`02858470  fffff800`015ef9a0 fffff800`015c4d40
serial
fffff880`02858470  fffff800`015ef9a0 fffff800`015c4d40
blbdrive
fffff880`02858470  fffff800`015ef9a0 fffff800`015c4d40
raspptp
fffff880`02858470  fffff800`015ef9a0 fffff800`015c4d40
rassstp
fffff880`02858470  fffff800`015ef9a0 fffff800`015c4d40
rdpbus
fffff880`02858470  fffff800`015ef9a0 fffff800`015c4d40
ks
fffff880`02858470  fffff800`015ef9a0 fffff800`015c4d40

 

// 同じく nt!MmAllocatePagesForMdlEx (fffff800`015ef900) を呼び出す可能性があるドライバ抜粋

kd> !for_each_module ".echo ${@#ModuleName} ;s -q ${@#ModuleName} L?${@#Size} fffff800`015ef900"
nt
fffff800`0199e940  fffff800`015ef900 fffff800`0199c1d0
dmvsc
fffff880`00c17188 fffff800`015ef900 fffff800`014d09c0
CLFS
fffff880`00e41070  fffff800`015ef900 fffff800`014b3550
CI
fffff880`00e41070  fffff800`015ef900 fffff800`014b3550
fffff880`011f13e8  fffff800`015ef900 00000000`00000000
HIDCLASS
fffff880`00e41070  fffff800`015ef900 fffff800`014b3550
volmgrx
fffff880`00e41070  fffff800`015ef900 fffff800`014b3550
amdxata
fffff880`00e41070  fffff800`015ef900 fffff800`014b3550
NETIO
fffff880`00e41070  fffff800`015ef900 fffff800`014b3550
NDIS
fffff880`011f13e8  fffff800`015ef900 00000000`00000000
msrpc
fffff880`011f13e8  fffff800`015ef900 00000000`00000000
ACPI
fffff880`011f13e8  fffff800`015ef900 00000000`00000000
pci
fffff880`011f13e8  fffff800`015ef900 00000000`00000000
Wdf01000
fffff880`011f13e8  fffff800`015ef900 00000000`00000000
WDFLDR
fffff880`011f13e8  fffff800`015ef900 00000000`00000000
vmbus
fffff880`011f13e8  fffff800`015ef900 00000000`00000000
winhv
fffff880`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