忍者ブログ
プログラミングとか日常とかの覚書っぽいなにか
[29] [28] [27] [26] [25] [24] [23] [22] [21] [20] [19]
×

[PR]上記の広告は3ヶ月以上新規記事投稿のないブログに表示されています。新しい記事を書く事で広告が消えます。

ちょっとWMIに触る機会があって調べていた時に「あれ?」と思ったことを一つ。


WMIについて調べると、MSDN内で日本語となると、以下のページが検索に引っかかります。

プロセス ID またはプロセス ハンドルを使用してジョブ オブジェクトを取得する方法
http://msdn.microsoft.com/ja-jp/library/dd296821.aspx


ここではオブジェクトの列挙に以下のようなコードを書いてますが
   while (pEnumWbemClsObj)
   {
      HRESULT hr = pEnumWbemClsObj->Next(WBEM_INFINITE, 1, &pclsObj, &uReturn);

      if(0 == uReturn)
      {
         break;
      }

      VARIANT vtProp;
      VariantInit(&vtProp);
      hr = pclsObj->Get(L"CollectionID",0,&vtProp,0,0);
      CHString str(vtProp.bstrVal);
      wcout << "Name : " << (LPCWSTR)str.Mid(1,str.GetLength())<< endl;
      VariantClear(&vtProp);
   }

COMに触ったことがあれば、これを見て「あれ、pclsObjは毎回Releaseしなくていいの?」って思うんじゃないかと思います。
上記のコードはWin32_NamedJobObjectインスタンスの列挙に関する部分ですが、同じコードの下の方にある Win32_NamedJobObjectProcess インスタンスの列挙でも同様にReleaseされない書き方になっています。

COMでのオブジェクト参照数のルールから考えれば、インタフェースポインタのアドレスを渡してそこにオブジェクトを受け取った場合には、そのオブジェクトは呼び出し元がReleaseしなければならないはず! しかし、本家MSDNで公開されているのだから、もしかしてこのメソッドは特別とか…? などと思ったので、ちょっと調べてみることに。

実際にサンプルの列挙部分を1000回ほどループさせて実行させたときにメモリ使用量がどうなるのかをProcess Explorerで調べてみたところ、以下のような結果に。

処理としては
   CoInitializeEx(COINIT_MULTITHREADED);
   for (int i=0; i<1000; i++) {
      WMI Locator取得・接続
      Win32_NamedJobObjectインスタンス列挙
      Win32_NamedJobObjectProcessインスタンス列挙
   }
   CoUninitialize();
というように、列挙部分のみを1000回繰り返します(MSDNサンプルでCOM初期化とクリーンアップのみをループの外に置いた感じです)。
結果は以下の通り、メモリ消費が最初の1.5MB程度からコンスタントに増加し、1000回終了時点で6MB程度に。明らかにメモリリークが発生していますね。
WMI_Test_Leak.png


次に、それぞれの列挙の中で、Next() で取得したオブジェクトをReleaseした場合、つまり上記のコードの部分で、
   while (pEnumWbemClsObj)
   {
      HRESULT hr = pEnumWbemClsObj->Next(WBEM_INFINITE, 1, &pclsObj, &uReturn);

      if(0 == uReturn)
      {
         break;
      }

      VARIANT vtProp;
      VariantInit(&vtProp);
      hr = pclsObj->Get(L"CollectionID",0,&vtProp,0,0);
      CHString str(vtProp.bstrVal);
      wcout << "Name : " << (LPCWSTR)str.Mid(1,str.GetLength())<< endl;
      VariantClear(&vtProp);
      pclsObj->Release();
   }
のように変更した場合。
WMI_Test_Normal.png

起動から終了まで、メモリ使用量は1.5~1.6MBでほぼ一定になってます。ということは、Releaseするのが正しいということです。


実はWeb検索で調べると、Releaseしてないサンプルが割と多くヒットするのですが(おそらく上記のMSDNのページを参考にしたんでしょうけど)、それであまり問題になっていないのはなぜかしら……もしかしてCOMクリーンアップ(CoUninitialize)まで実行するとメモリが解放されるのでは? と思い立って、実際に試してみました。
   for (int i=0; i<1000; i++) {
      CoInitializeEx(COINIT_MULTITHREADED);
      WMI Locator取得・接続
      Win32_NamedJobObjectインスタンス列挙
      Win32_NamedJobObjectProcessインスタンス列挙
      CoUninitialize();
   }
上記のように、ループ反復ごとにCOM初期化とクリーンアップを実施すると…。
WMI_Test_Uninit.png

確かに、メモリ使用量が1.5~1.7MBの範囲に留まっているので、CoUninitializeの時にメモリが解放されているようです。I/Oが発生しているのは毎回COM初期化とクリーンアップ実施しているために、内部で何か処理が走っているせいでしょうかね。

ですが、実際のところ、COMを使用してる場所がここだけとは限らないわけで、すでにCOM初期化を実行していた場合、つまり
   CoInitializeEx(COINIT_MULTITHREADED);
   for (int i=0; i<1000; i++) {
      CoInitializeEx(COINIT_MULTITHREADED);
      WMI Locator取得・接続
      Win32_NamedJobObjectインスタンス列挙
      Win32_NamedJobObjectProcessインスタンス列挙
      CoUninitialize();
   }
   CoUninitialize();
のようにした場合は、いちばん最後のCoUninitializeが実行されるまでは解放されなくなってしまいます。CoUnitinializeによる解放に頼ることなく、正しくReleaseするべきだと言うことのようです。

拍手

PR

コメント


コメントフォーム
お名前
タイトル
文字色
メールアドレス
URL
コメント
パスワード
  Vodafone絵文字 i-mode絵文字 Ezweb絵文字


トラックバック
この記事にトラックバックする:


忍者ブログ [PR]
プロフィール
HN:
はむぱい
職業:
ソフト作ったりしてる人
Twitter
最新CM
[06/09 replica rolex oyster perpetual datejust]
[06/09 bracelets imitation cartier love]
[06/09 replica the oyster perpetual datejust]
[06/09 datejust rolex oyster perpetual]
[06/09 replica gold love bangle]
カレンダー
11 2017/12 01
S M T W T F S
1 2
3 4 5 6 7 8 9
10 11 12 13 14 15 16
17 18 19 20 21 22 23
24 25 26 27 28 29 30
31
ブログ内検索
あ~いい漢字