技術は記憶の彼方へと

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

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

前回パフォーマンスモニターで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に近いミドルウェアとか作る際には注意深くコーディングをする必要がありますね(それ以外の上層アプリケーションも気を使ったアプリ作ってくれると下層はちょっとは楽になるんだけれどもあんまり気にしないよね...)
それでは愚痴もちらほら出てきたところで、今回はこの辺で。

【備忘録】パフォーマンスモニターを使ってCPU状況を把握する

前回の記事が2022-06-11って事なので、一年ほど空きました...
何というか、転職したり配属変わったりとバタバタしてまして、前回記事にしたHackerRankもそんなに触れてない状況です。
さて、今回は配属された先がWindows系の組み込み機器のようなものを作ってるようでして、今までLinuxで生きてきた人間にとってはちょっと新鮮な感じがしています。
その中でCPUスパイクやメモリリークといった事象が発生していないかを見ることが多くなったので、Windows標準で入っているパフォーマンスモニターを使うことが多くなってきたので、備忘録に残そうと思います。
どういうカウンタの値見てるかとかに絞って情報を記載しておこうと思います。メモリは次回にでも出来たらいいなぁ。

2023-09-23 追記
メモリに関しての記事をようやく書きました。
cloudvelse.hatenablog.jp
Win系システムってことで、バッチファイル(Batファイル)君も扱うことになってるわけですが、あれは....なんなんだ。人がやるものなのか?
もし、精神的に余力があるなら....記事に....したくないなぁ。どうせ職場の環境とうちの環境違うから動かないんでしょ?知ってるからな。
前置きはこの辺で、本題行きましょうか。

目次


パフォーマンスモニター


Windowsに標準で入っている、OSのパフォーマンスの推移を総合的に確認することができるツールでありあまり負荷をかけることなくパフォーマンスログを取得することができるツールである。
システムの状況をタスクマネージャ等で常時監視せずに、ファイルに出力させるなどして確認に使うことができる便利なツールとでも思えばいいかもしれません。
基本的にログは.blg拡張子のバイナリ形式で保存されますが、設定でCSV等に変更することは可能ですがログ取得開始時に起動しているプロセス以外で後から起動したプロセスについては記録できなくなってしまう仕様となっている為、バイナリ形式で取得してからrelogコマンドで変換して解析することをお勧めします。
パフォーマンスモニターのツール上でどのカウントを取得するか、採取周期はどうするかといったことを設定してデータコレクタセットを作成することもできますが、一つずつ追加して...としていくのが私は大変だと思いましたので、コマンドと取得したいカウンタをリストにしたTEXTファイルを用いる方法をお勧めします。
方法については、下記Microsoft社のサポートブログが参考になると思います。

パフォーマンス ログ収集 | Microsoft Japan Windows Technology Support Blog

CPU(プロセッサ)全体に関するカウンタ


今回の本題です。CPU利用率やスパイク調査等で私が主に使用しているカウンタについてを見ていきます。

\Processor Information\ % Processor Utility

システム全体のCPU利用率を表します。他の技術ブログなどでは、\Processor(_Total) % Processor Timeが紹介されていることが多いです。
どちらも同じような値を示すものではありますが、Processoe Informationはシステムの高性能・高機能化に対応するためWindows Server 2008 R2 から追加されたパフォーマンスカウンターであり、それ以降のシステムであればこちらを活用することをMicrosoft社として推奨しているようです。
というのも、Processorカウンタは64コア以上のCPUや、NUMA(Non-Uniform Memory Access)に非対応だったりするようです。
クロック周波数が使用状況によって変化するCPUに対応しており、タスクマネージャ上で表示されているCPUの「使用率」と参照している値が同じになります。
パフォーマンスカウンターがベースのクロック周波数を基準に作成されるため、負荷状況によってクロック周波数が変化するCPU搭載の場合は100%を超える値が表示される可能性があります。
※なおタスクマネージャでは、100%を超えた場合は切り捨てて100%で表示する仕様のようです。

常に閾値を超えるような場合にはCPUボトルネックが疑われます。
この値は、アプリケーション処理のためにCPUが使用されたことを意味していて高い値でありかつパフォーマンスに問題がある場合にはCPUアップグレードや負荷分散、又は不要なサービスの停止を検討する必要があると考えられるでしょう。
ただ、後述するランキューの値が低くOS的にパフォーマンスに問題がない場合もあります。

\Processor Information\ % Processor Time

CPUがIdle以外のスレッドを処理のするために使用された経過時間の割合を表します。
このカウンタはクロック周波数について意識していない為必ずしも上記のProcessor Utilityと一致するわけではない点に注意が必要です。
例えば、単位時間を10秒と雑に決めた場合に2秒間CPUが利用されれば20%と表示する。といった動作をするカウンタとなります。この例は雑に基準単位時間を決めましたが本来はもっと詳細な時間で決められています。
あくまでどれくらい使用されていたのか、どれくらいの割合でプロセスに利用されていたのかを参考程度に知るための指標となります。

\System\Processor Queue Length

CPUの処理を待つスレッド数、ランキューを表しています。あくまで処理待ち数であり実行中のスレッドは含まれません。
あくまでカウンタ採取時の瞬時値が表示されるわけですが、上昇傾向や常に高く維持されているような場合はCPU負荷が大きくなっている状態が疑われます。
マルチコア/マルチスレッドなシステムの場合は閾値も比例的に高くなりますが、CPUを占有する遅い処理やアクセス過多によりCPUボトルネックとなっている状態を疑い、ミドルウェアやアプリケーションにおける調査を検討する必要があると考えられます。

ただ、本指標は瞬間瞬間だけを見て判断ができる指標ではありません。
待ち行列というものは、リトルの性質と呼ばれる重要な性質があります。
それは、長期間観測した待ち行列の長さの平均が、システム負荷量の処理の積として得られるというものであり、以下の等式で説明されます。
<平均系内客数> = <平均到着数> × <平均サービス時間>
ここで言うところの、<平均系内客数>は平均の待ち行列長、<平均到着率>は単位時間当たりの処理要求数の平均(いわゆるシステム処理負荷量)、<平均サービス時間>は1つの処理を行うのにかかった時間平均(処理遅延)に相当するものとして計算をすることによってシステムの負荷状況や処理遅延の状況を知ることができるということがわかると思います。

上記から、この指標を判断するには瞬時的な上下なのか長期的な状態であるのかから判断をしていくことになる。
急上昇後にすぐに降下する。いわゆるグラフ等で見た際にスパイク状となっている場合については、実行待ちとなるスレッドは短時間で解消されており問題となる状態ではないと判断ができます。
逆に、常時高い傾向がみられる場合や上昇し続けるような傾向にある場合には、各アプリケーションやミドルウェアの調査を実施したほうが良い状態であると判断ができます。

つまるところ、パフォーマンスの体感ともいえる部分は最終的に処理遅延で決まってくる。処理待ち行列が多いこと自体が問題ではない。ということになりますので、注意が必要です。

\Processor\Interrupts/Sec

プロセッサが処理した1秒当たりのH/W割り込みの回数を表します。
割り込みが発生するとそれまでに実行されていたプロセスを一時停止させて処理を行うことになる為、頻発するとプロセッサにおける処理効率に影響を及ぼすことになってしまいます。
もし頻繁に発生するようなことがあった場合には、最近追加されたデバイスドライバ等を無効化や削除して変化があるのかを確認してみることをお勧めします。
変化があった場合には、そのデバイスドライバを通じて行われる割り込みがCPUをボトルネックにさせている原因であると考えることができるため処理について調査し改善を検討することができるでしょう。

プロセス毎のCPU使用状況


これまで記述してきた、プロセッサ全体での使用状況からCPUにボトルネックの傾向がみられることやスパイクがみられる場合にどのプロセスが影響をしているのかを知る為の指標となります。
ただ、これらのカウンター値は上限がCPUコア数×100となっておりCPUコア数によっては100%を超える値を取る可能性があります。例として16コアCPUの場合、0~1600の範囲での値となります。

\Process(プロセス名)\ % Processor Time

プロセスのスレッドが命令を実行するためにプロセッサを消費する経過時間の割合を表しています。
Processor TimeカウンタはProcessorオブジェクト、Processor Informationオブジェクトにもあるが、こちらはインスタンスとして指定したプロセス単体での使用率を表示するものであるため、大量に消費しているプロセスの特定に役立つカウンタとなります。

\Process(プロセス名)\ % Privileged Time

プロセスがカーネルモード(特権モード)で実行するためにプロセッサを消費する時間の割合を表します。
主に、カーネルモードで動くプロセスはOSの基本動作を担当するものであることがほとんどであり、起動するサービスを停止させる等対策ができないわけではないができる対策に限りがあることや、不用意にサービス停止をするとOS自体の起動・動作に影響を及ぼしかねないことに注意が必要となります。

\Process(プロセス名)\ % User Time

プロセスがユーザモードで実行するためにプロセッサを消費する時間の割合を表します。
おもに、アプリケーション動作を担当するものとなる為ユーザモードの処理がプロセッサを圧迫している場合は起動するアプリケーションを減らすことや該当するプロセスの処理内容を見直し最適化する等の対策を考えることができます。

最後に


今回は、PerfmonにおけるCPUに関連する項目についてを見ていきました。
実際にこれらを解析してスパイクしているプロセスを調査したり対策を講じたりということを行っている身として、どのカウンタを見ればいいのか等非常に多く探すのも大変になりますね。
Linuxで似たようなことをやろうと思うと、syslogを見たりvmstat、top、sarあたりを確認していけば同じような解析ができると思います。
sarだと、Ubuntuとかで正しく表示できない...なんてことがあったような気もします。テキストにしてしまえば問題はなかったかと思いますがディストリビューションで差分があるなんてあるあるですね。
Windowsだとこれらである程度は見れますが、これだけでは情報が足らないなんてこともよくあります。
そんな時は、WPRを使ってみたりProcess Exploreを使ったりとMicrosoft社が出してる解析ツールをあれこれ駆使して詳細まで追いかけ続ける必要が出てきてしまうので大変ですね。
最近は、バッチファイルに苦しめられている為備忘録を作っておきたい思いはあるのですが、何が悪いのかわからなかったりネット上での参考ソースをそのままコピーしても動かないってことが多発するせいで役に立たなさそうなんですよね....
何というか、狂気的な雰囲気を感じています。
Linuxシステムに帰りたい...そう願う日々です。
それでは、今回はこの辺で。

HackerRankやってみた

プライベートでコードを書く余力が全然なく、更新ができてませんでした。
そんなこんなで、コードに触れる機会を増やそうと、HackerRankをやってみたので、感想でも書こうかなぁと。。

そもそもHackerRankって?


https://www.hackerrank.com/
プログラミングの問題を解くことができるサイトのことです。
解いた問題数でランキング化されたりします。
C, C++, Java, Python, PHP, Swiftなど使える言語も多く、学習なんかにも使えると思います。

特徴

問題文がすべて英語。
質問をすることが可能。
事前に一部のコードが与えられている。

それぞれ見ていきます。

問題文がすべて英語

記載の通りで、問題文はすべて英語で記載されています。
また、Copy等ができないようになっているので、Google翻訳などで確認がしづらい分、問題の内容の理解が大変になっているように感じました。
英語が得意な方であれば問題が何かわかるかと思いますが、苦手だと、問題文の理解に回答時間を持っていかれてしまうかもしれません。

質問をすることが可能

質問を投稿する機能が存在しています。
もちろん英語ではありますが、世界各国の複数の方が利用しているため、その問題に対する質問や回答が多数あります。
記載されている英文の理解ができれば、回答に役立つかもしれません。

事前に一部のコードが与えられている

AtCoder等の他のプログラミング問題のサイトとの違いはここだと思います。
事前にコードの一部が与えられており、関数やメソッド等を記載するといったスタイルになっています。
もちろん、問題の指示通りに動作すればいいので、すでに乗っているコードを全削除して一から記載して解くといった方法も可能ではあります。
関数の意味や引数について考える必要があり、少し面倒な部分はありますが、事前にフォーマットがある程度決まってる環境でのコーディングが得意であれば、やりやすいと感じつ部分もあるのかもしれません。

実際にやってみた感想


まず、問題がわかりにくい。。。
私は、英語力がそんなに高くないので、問題の理解がまずネックになりました。
制限時間がある中で、問題文の解読に少し時間を取られ、ロジックを考えたりする時間が短くなってしまうということがあり、なかなか進めるのが難しいと感じました。
やってみて、解けたと思っていてもテストケースでエラーが出ていたりして、再度考え直しになることも多々あり、苦労することも多かったですが、成功した際の達成感はよかったですね。
コードが事前に与えられているので、そこから呼び出され方や要求されている機能部分とのすり合わせも考えないといけなかったので、少々手間取ってタイムオーバーになったこともありました。

最後に


全然コードに触れられてない身として、初めて見ましたが、いい練習になりそうだと感じました。
少々、問題の癖や英語の読解力に壁は感じていますが、言語数も多いので複数言語の練習としても使えそうです。
ただ、少し悲しいのは、これはポートフォリオとして使えないといったことですね。
GitHubリポジトリを作って、自身の回答方法を載せておくとかしないと実績として残せないのは少し残念だなぁと感じましたね。
使えると、色々と便利そうではあったのですが。仕方ないので、練習用と割り切って今後も使ていくことにします。
今回は、軽く触ってみた感想だけになってしまいましたが、よければ使ってみてください。
それでは、今回はこの辺で。