【VBScriptの真髄】WMI大量クエリのメモリリークを根絶する「参照管理」の極意
VBScriptを「古い言語」と侮るなかれ。WSH(Windows Script Host)環境で長期間稼働する監視ツールや、大量のファイル・プロセスを叩く自動化スクリプトにおいて、メモリ管理の甘さは即座にシステムの死を招く。
特に `SWbemServices` を介したWMIクエリは、開発者が無意識のうちにメモリを食いつぶす「時限爆弾」を仕込みやすい領域だ。今日は、数万件のオブジェクトを扱う環境でもメモリを安定させ、1週間連続稼働させても揺るがない堅牢なスクリプトを構築する「参照リーク完全根絶」の作法を伝授する。
—
1. なぜWMIクエリでメモリが肥大化するのか
多くのエンジニアが犯す過ちは、`For Each` ループの中にクエリの取得ロジックを詰め込み、オブジェクトの参照を放置することだ。
VBScriptのガベージコレクションは非常に寛容だが、WMIの `SWbemObjectSet` は別格だ。特に `Win32_GroupUser` や `CIM_DataFile` のように、階層が深く、大量のインスタンスを生成するクエリでは、「クエリ結果のセット」と「個々のオブジェクト」の参照がメモリに残り続け、プロセスが肥大化する。
これを防ぐ唯一の解は、「明示的な破棄(Nothing代入)」と「最小限のスコープ」の徹底である。
—
2. 参照リークを根絶する「プロフェッショナル・パターン」
以下のコードは、大量のWMIオブジェクトを安全に処理するためのテンプレートだ。ここでのポイントは、ループ内でのオブジェクト再利用を避け、使い終わったオブジェクトを即座に開放することにある。
Option Explicit
‘ 監視ツール等で安定稼働させるためのメモリ管理テンプレート
Sub ProcessWMIQueries()
Dim objWMIService, colItems, objItem
‘ WMI接続の確立
Set objWMIService = GetObject(“winmgmts:\\.\root\cimv2”)
‘ クエリ実行
‘ 大量データを扱う場合、クエリは必要最小限のプロパティに絞ること (SELECT は悪手)
Set colItems = objWMIService.ExecQuery(“SELECT Name, Path FROM Win32_Process”, , 48)
‘ 参照カウンタを意識したイテレーション
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
End Sub
押さえるべき3つの鉄則
1. `SELECT ` の禁止: 必要なプロパティのみを指定せよ。“ は不要なデータまでメモリに展開し、パフォーマンスを著しく低下させる。
2. ループ内破棄: `For Each` 内で取得したオブジェクトは、次のイテレーションへ回す前に `Set objItem = Nothing` で参照を断ち切るのが鉄則だ。
3. フラグの活用: `ExecQuery` の第3引数に `48` (wbemFlagReturnImmediately + wbemFlagForwardOnly) を指定せよ。これにより、メモリ上に全結果をキャッシュせず、ストリーミングに近い状態で処理が可能になる。
—
3. 実践:ログ出力とエラーハンドリングの共存
プロダクションコードでは、ファイル書き込みが絡むことが多い。ここでエラーが発生し、`Set` 命令が飛ばされるとメモリリークの温床になる。必ず `On Error Resume Next` を制御し、確実に破棄フェーズへ到達させる設計が必要だ。
‘ エラー時でもリソースを確実に解放する設計
Sub SafeQueryExecution()
Dim objWMIService, colItems, objItem
On Error Resume Next ‘ エラー発生時も破棄処理へ飛ばす
Set objWMIService = GetObject(“winmgmts:\\.\root\cimv2”)
Set colItems = objWMIService.ExecQuery(“SELECT FROM Win32_LogicalDisk”, , 48)
For Each objItem In colItems
‘ 業務ロジック (ここにファイル書き込み等を実装)
‘ …
Set objItem = Nothing ‘ 個別破棄
Next
‘ 最終解放処理
Set colItems = Nothing
Set objWMIService = Nothing
On Error Goto 0
End Sub
—
4. チーフアーキテクトからの助言
VBScriptは低レイヤーに近い挙動をするため、現代のManaged Language(C#やPython)に慣れたエンジニアほど「メモリは勝手にやってくれる」という油断が致命傷になる。
- スクリプトの分離: 1つの大きなスクリプトで全てを完結させようとしないこと。大量のWMIを叩く処理は、小さな単位でスクリプトを別プロセスで起動し、終わったら死ぬ設計(Fire-and-Forget)にするのが、長期間稼働の最強の安定化策だ。
- ログの監視: `tasklist /FI “IMAGENAME eq wscript.exe”` で稼働中のメモリ使用量を定期的にモニタリングする習慣をつけよ。
この作法を守れば、VBScriptは単なるレガシーコードではなく、軽量かつ強力な業務自動化の武器として、あなたの現場を支え続けるはずだ。
コードを書くときは、常に「このオブジェクトはいつ死ぬのか」を自問せよ。それが一流のエンジニアへの道だ。
