VBScriptの亡霊を飼いならす:FSOループ処理におけるメモリ最適化の極致
VBScriptは、現代のモダンな言語から見れば「化石」のように見えるかもしれない。しかし、レガシーシステムの深淵で戦う我々にとって、この言語は依然として強力な武器だ。
特に、数万件のファイル操作を伴うバッチ処理において、FSO(FileSystemObject)の扱いを誤ることは、即座に「スクリプトのハングアップ」や「プロセス肥大化によるOSの悲鳴」という形でしっぺ返しを食らう。
今日は、FSOをループ内で回す際に、メモリリークを回避し、プロセスをクリーンに保つための「戦術的最適化」について伝授する。
—
1. なぜFSOは「重い」のか?
VBScriptにおける`Scripting.FileSystemObject`は、COMラッパーである。ループ内で安易にインスタンスを生成し、参照を適切に破棄しないことは、COM参照カウント(Reference Counting)の墓場を築く行為に等しい。
特に、`For Each`ループ内で`File`や`Folder`オブジェクトを個別に`Set`し続けると、VBScriptのガベージコレクタ(GC)は追い詰められる。結果、メモリ使用量は右肩上がりとなり、スクリプトの実行時間は指数関数的に増大する。
—
2. 最適化の原則:スコープと再利用
メモリリークを防ぐための黄金律はシンプルだ。
- インスタンスはループの外で生成し、使い回す
- ループ内の参照変数は、反復ごとに必ず明示的に解放する
- パス操作はCOMオブジェクトに頼りすぎない
実装例:最適化されたファイル走査パターン
多くのエンジニアが犯すミスは、ループ内で`fso.GetFile(path)`を乱発することだ。これでは、ファイルごとに新規のCOMオブジェクトが生成される。以下のコードは、そのオーバーヘッドを最小限に抑えるためのテンプレートである。
‘ — メモリ効率を最大化したFSOループ処理 —
Option Explicit
Sub ProcessLargeFileDirectory(ByVal targetFolder)
Dim fso, folder, file
‘ 1. インスタンスは一度だけ生成する
Set fso = CreateObject(“Scripting.FileSystemObject”)
If Not fso.FolderExists(targetFolder) Then Exit Sub
Set folder = fso.GetFolder(targetFolder)
‘ 2. For Eachによる列挙はメモリ効率が良い(コレクションのイテレータを利用)
For Each file In folder.Files
‘ ここで重い処理を行う
‘ fileオブジェクトに対する操作は最小限にする
‘ 3. 【重要】ループの最後で個別のオブジェクト参照を明示的に解放する
‘ これを怠ると、参照カウントがループ回数分だけ残留する可能性がある
Set file = Nothing
Next
‘ 4. ループ終了後に親オブジェクトをクリーンアップ
Set folder = Nothing
Set fso = Nothing
End Sub
—
3. なぜ「Set = Nothing」を叫ぶのか
「VBScriptはスクリプト終了時に解放されるから意味がない」という意見がある。これは、小規模なスクリプトでは正しい。しかし、数万件を処理するプロセスにおいて、「次にGCが発動するまでのメモリ占有」を制御できないことは致命的だ。
明示的な`Set obj = Nothing`は、単なる気休めではない。これは「このCOM参照カウントを今すぐデクリメントせよ」というOSへの指令である。これにより、解放されたメモリ領域を即座に再利用可能にし、プロセスの常駐メモリサイズを一定に保つことができる。
—
4. チーフアーキテクトからのさらなる提言
A. 文字列連結の罠
ループ内で `log = log & file.Name & vbCrLf` のような連結を繰り返してはならない。これはメモリ上に巨大な断片化を引き起こす。大量のログを吐くなら、`Scripting.Dictionary`に蓄積するか、`ADODB.Stream`で直接ファイルへ書き出すのがプロの仕事だ。
B. WMI/Windows APIの活用
もしFSOの速度がボトルネックになるなら、`WScript.Shell`の`Exec`メソッドで`dir /b /s`を叩き、その結果をパイプで受け取る方が、COMオブジェクトを数万回生成するよりも数倍高速である場合が多い。
C. プロセスの生存期間
どうしてもメモリリークが防げない極限の環境(例えば、24時間稼働の常駐監視ツール)では、「あえてメインスクリプトから子プロセスを起動し、一定処理後に自滅させる」というアーキテクチャを採用すべきだ。これこそが、レガシー環境における究極の安定化手法である。
—
結びに代えて
VBScriptは古いが、その挙動を完全に掌握したエンジニアにとっては、極めて予測可能性の高い言語だ。メモリは有限であり、リソースは常に枯渇する。その中で、いかに無駄なCOMインスタンスを生成せず、いかに迅速に解放するか。
その「美学」を追求することこそが、システムを延命させる唯一の道である。コードは書くものではない。設計するものであることを忘れるな。
