技術は記憶の彼方へと

覚えたつもりですぐに忘れるエンジニアの備忘録

【備忘録】パフォーマンスモニターでメモリ使用率を見る

前回パフォーマンスモニターでCPUスパイク等の確認で見ているカウンタについてを記載して、次あたりでメモリについて書きたいみたいなことを言っていたような気がしたので、今回はメモリリークが発生していないか確認する際に見ているカウンタについてを書いていこうと思います。
前回パフォーマンスモニターについてのざっくりとしたことは書いた気がするので、そちらも見ていただければ幸いです。

cloudvelse.hatenablog.jp

目次


物理メモリ


物理メモリは文字通り、物理的に搭載されているメモリとなります。
このメモリの使用状況を確認していくことはメモリリークの調査における最初の一歩であると思っています。

Memory\Available Bytes

プロセスが利用可能な物理メモリの空き領域をByte単位で表すカウンタとなっています。(ほかの単位でMBやKBで表すカウンタもあります)
Available Bytesは利用可能なメモリの空き領域を表している為、この値が大きければ各アプリケーションにそれだけ割り当てることができるといえますが、ボトルネックとなっている場合には小さな値をとることとなります。
それらを踏まえて、メモリリークが発生しているか否かで見る場合にはこのグラフが右肩下がりとなり続けている場合にはリークを疑う一つのアイテムとなり得ます。
徐々に値が小さくなるだけでリークだと断定してしまうことは難しいためほかのカウンタも参照して判断する必要があります。

システム領域


システム領域では、ページアウトが可能なページプール領域と物理メモリ以外に配置不能(ページングできない)な非ページプール領域に分けてメモリの使用量を確認していきます。
以下のいずれかのカウンタが上昇している場合にはメモリ不足の原因となっていることが考えられます。
目安として、ページプールの使用率と非ページプールの使用量が物理メモリに対して8割を超えている場合、または5GBを超えている場合にはシステム領域でメモリリークが発生し大量のメモリが消費されていると考えることができます。
基本的にOS動作に関する部分となってくる為、再起動を行い開放させる等の対策しかなくあまりできる対策は少ないものとはなりますがソフト要因か否かの判断材料として確認しておきます。

Memory\Pool Paged Bytes

ページング可能なページプール領域にコミットされているメモリの大きさを表しています。
基本的にページング可能な領域でのメモリ利用になっている為、常時使用しているか仮想メモリが不足していてページングされていない等が考えることができます。
仮想メモリ量の設定を見直すなどで改善する可能性があります。

Memory\Pool Nonpaged Bytes

ページング不可な非ページング領域にコミットされているメモリの大きさを表しています。
こちらは、対応するカーネルオブジェクトが割り当てられている限り物理メモリ上に存在することが保障されているアドレスで構成されています。

ユーザ領域


各プロセスが実際に扱っているメモリの推移についてを確認します。
システム領域の状況を確認したうえで、大きな問題がみられない場合にソフト原因を疑って確認していくことになります。
ここでは全体についてみていきますが、全体を見た際にリークの傾向が確認できる場合には、_Totalの部分を各プロセス名にすることでそれぞれのプロセスのメモリ使用量が確認できますのでそこからリークを引き起こしているプロセスを確認し、ソース内でメモリ解放が正しく行えているのかや、解放漏れとなるような条件分岐が起きていないか等の確認を進めていくことになります。(これがなかなか大変、やめたくなります)

Process(_Total)\Working Set

アプリケーションが使用するメモリのうち、物理メモリ上で確保されているメモリ領域をByte単位で表したものとなります。
実際に確保されている量を表している為、右肩上がりのグラフとなっている場合にはメモリリークを引き起こしている可能性があるプロセスがある判断ができると思います。

Process(_Total)\Praivate Byte

プロセスが割り当てている他プロセスとは共有することができない仮想メモリ使用量をByte単位で表しています。
物理メモリではなく仮想メモリの増加量についてになりますが、こちらが増え続けている場合においても枯渇するとページングできない状態となり一時的に不要になったリソースも物理メモリに残り続けてリークを引き起こす原因となりかねない為、確認していく必要があります。
実際に確保している仮想メモリの量で、右肩上がりのグラフとなっている場合はメモリリークを引き起こしているプロセスがある可能性があります。

他で見ているカウンタ


Process(_Total)\Handle Count

ハンドルリークが発生していないかの確認を行います。
Windows APIを用いてカーネルオブジェクトを作成した場合、Windowsカーネル内部でカーネルオブジェクトを作成した後操作するためのハンドルを返します。
これは、直接カーネルオブジェクトのポインタを返すことはせずにハンドルを経由させることで保護する目的で行われています。
それをプロセスハンドルテーブルに、オブジェクトへのポインタや参照カウント等を登録することで利用していきます。
当然、同じカーネルオブジェクトが複数個所から参照される場合には参照カウントが増えていき、ハンドルを閉じればカウントも減る動きを取ります。
参照カウントがなくなれば、そのオブジェクトに関連するリソースも解放されるといった動きを取ります。
ハンドルの閉じ忘れが発生すると、いつまでもそのリソースを解放せずにプロセス内に不要なリソースを蓄積して残り続けるハンドルリークとなります。

ハンドルリークの説明が長くなりましたが、本カウンタでは実際にプロセスが開いているハンドル数を表しています。
これが閉じられることなく増え続けるグラフとなる場合には、ハンドルリークを疑うこととなります。
ただし、短時間しか実行されないようなプロセスであった場合は、これらのリークの影響は表に出て来辛い傾向にあります。
プログラム終了時にOS側で割り当てたリソースの解放を行う処理が実施されるためです。
ただ、サーバや長期稼働前提のシステムである場合、終了時のリソース解放に頼るのは非効率ですし、不可能と考えてもよいかもしれません。
そういった場合に突然問題を引き起こす原因となったということになりかねない為確認を行います。
こういった場合って、テスト環境では出てこなかったりして解析困難だったりして面倒なんですよねぇ

Memory\Cache Bytes

物理メモリ上にあり、アクティブ状態となっているシステムファイルキャッシュの部分をByte単位で表しています。
このキャッシュが膨らみ続けるとメモリ利用可能領域が減っていくこととなりリークの原因となる為に確認をしています。
あまり詳しい情報が出てこない為、あくまで目安程度に確認する。で問題はないように感じますが、念のため。

最後に


今回はメモリの使用状況を確認するカウンタについて記載していきました。
あまり見ないももいくつかありますが、リーク系の不具合って再起動とかするとリソース解放をOSが行う都合上表に出てこないことが多く、動作重くなったとかフリーズしたって客先から言われて発覚したとかざらじゃないんですよね...
最近のWindowsとかだと高速スタートアップの機能等で再起動では前回のメモリ状態を保持したまま再起動することがあり、不具合解消や修正適用の為再起動してねって言っても適用されないことがちょくちょくみられるようになって、完全シャットダウンしてくださいとかお願いする事も出てきて困惑してます。
再起動でリソース解放せしないで前回の状態を引き継ぐって正直再起動の意味あるのか?って文句も言いたくなりますね。
そんなことは置いておいて、リーク不具合って試験環境で出てこなかったりするのに重大不具合につながるのが辛いですが、コーディングスキル次第なところもあったり確認次第な部分があるので技術力は重要な課題です。
静的解析ツールとかの解析ツールで全部見つけてくれれば楽なんだけれども、そうもいかないのが現実。
ドライバやOSに近いミドルウェアとか作る際には注意深くコーディングをする必要がありますね(それ以外の上層アプリケーションも気を使ったアプリ作ってくれると下層はちょっとは楽になるんだけれどもあんまり気にしないよね...)
それでは愚痴もちらほら出てきたところで、今回はこの辺で。