【入門編】【参照リーク完全根絶】SWbemObjectSet の safe-enumerate と明示的破棄によるWMI大量クエリ時のメモリ肥大化防止 – VBScript (Visual Basic Scripting Edition)解析バイブル

スポンサーリンク

VBScriptを掌握せよ:WMI大量クエリでメモリを溶かさない「参照管理」の極意

こんにちは。現場で「スクリプトが数日動くとメモリを食い尽くして落ちるんだ」と嘆くエンジニアを何人も救ってきた、アーキテクトの先輩です。

VBScriptは古い言語だと思っていませんか?確かにそうかもしれません。しかし、Windowsの心臓部であるWMI(Windows Management Instrumentation)を直接叩けるこの言語は、適切に扱えば今でも最強の自動化ツールです。

今日は、VBScriptで「中級者」へとステップアップするための登竜門。「WMIによる大量データ取得時のメモリリーク完全撲滅法」を伝授します。

1. なぜ、WMIスクリプトは「肥大化」するのか?

WMIから `Win32_Process` や `CIM_DataFile` などを取得するとき、皆さんは以下のようなコードを書いていませんか?

Set colItems = objWMIService.ExecQuery(“Select from Win32_Process”)
For Each objItem in colItems
‘ ここで処理
Next

一見、何の問題もなさそうですよね。しかし、これが「数千件のファイル情報」や「長期間稼働する監視スクリプト」になると話は別です。

VBScriptの背後にあるCOMオブジェクト(SWbemServicesなど)は、私たちが想像する以上に「参照の解放」に神経質です。`For Each` ループの中で `SWbemObjectSet` がメモリ上に残り続け、ガベージコレクションが追いつかない。結果、スクリプトのプロセスが数日かけてメモリを食い尽くす。これが「参照リーク」の正体です。

2. 参照リークを根絶する「3つの鉄則」

現場で生き残るスクリプトを書くために、以下の3つを体に染み込ませてください。

1. 明示的な破棄(Nothing): 使い終わったオブジェクトは、その場で `Set obj = Nothing` する。
2. イテレータの使い回しを避ける: 大量クエリ時は、コレクション全体を保持せず、必要に応じて破棄する。
3. Variant型の罠に落ちない: 暗黙的に生成される一時オブジェクトを意識する。

3. 実践:メモリを食い尽くさない「安全な列挙」コード

それでは、メモリリークを完璧に防ぐための「模範解答」をお見せします。ポイントは、ループが終わるたびにオブジェクトを明示的に解放し、参照の連鎖を断ち切ることです。

‘ — メモリリーク防止版 WMIクエリのテンプレート —
Option Explicit

Dim objWMIService, colItems, objItem

‘ WMIサービスへの接続(ここは一度だけ行う)
Set objWMIService = GetObject(“winmgmts:\\.\root\cimv2”)

‘ クエリ発行
Set colItems = objWMIService.ExecQuery(“Select from Win32_Process”, , 48)

‘ 安全な列挙(Safe-Enumeration)
If colItems.Count > 0 Then
For Each objItem In colItems
‘ ここで重い処理を行う
WScript.Echo “プロセス名: ” & objItem.Name

‘ 【重要】ループ内で個別のオブジェクトを即座に解放
Set objItem = Nothing
Next
End If

‘ 【重要】大元のコレクションも最後に必ず解放
Set colItems = Nothing
Set objWMIService = Nothing

WScript.Echo “処理完了:メモリはクリーンな状態です。”

なぜこれでメモリが安定するのか?

`For Each` の中で `Set objItem = Nothing` を呼び出すことで、VBScriptが裏側で保持している「COMインターフェースへの参照」を即座に切断しています。これにより、次のループに進む際に不要なメモリ領域がOSへ即座に返却されます。

4. 初学者が陥りやすい罠:エラーハンドリングの欠如

大量のデータを扱う際、途中で「権限エラー」や「アクセス拒否」が発生することがあります。エラーが発生した時に `Set … = Nothing` をせずに終了すると、その時点でメモリリークが確定します。

必ず `On Error Resume Next` を活用し、どのような状況でも `Nothing` を呼び出す構造にしてください。

On Error Resume Next

‘ … 処理 …

‘ 終了処理は必ず通る場所に書く
Finally:
Set objItem = Nothing
Set colItems = Nothing
Set objWMIService = Nothing

最後に:先輩からのアドバイス

「動けばいい」コードと「長く愛される」コードの差は、こうした小さな後片付けの積み重ねにあります。

VBScriptは、書いたコードの責任をすべてプログラマが負う、非常にストイックな言語です。だからこそ、オブジェクトのライフサイクルを制御できた時の喜びはひとしおです。

ここをクリアすれば、あなたはもうただのスクリプト初心者ではありません。システムを安定して支える「業務自動化エンジニア」の仲間入りです。次は、WMIイベント監視や、さらに深いCOMオブジェクトの制御に挑戦してみてください。応援しています!

タイトルとURLをコピーしました