配列の限界を突破せよ:ReDim Preserve が招く「メモリの断片化」との闘い
VBAという言語は、現代のメモリ管理が自動化された高水準言語に比べれば、極めて「プリミティブな肉体」を持っている。特に、件数が不定のデータを扱う際に多用される `ReDim Preserve` は、初心者には魔法の杖に見えるかもしれないが、シニアエンジニアにとっては「諸刃の剣」である。
今回は、この `ReDim Preserve` の裏側で何が起きているのか、そして大規模データを扱う際、いかにしてメモリ効率を極限まで高めるかについて、アーキテクトの視点から説く。
—
1. ReDim Preserve の「残酷な真実」
`ReDim Preserve` を実行するたび、VBAのランタイムは裏で以下の処理を行っている。
1. 新しいサイズのメモリ領域を確保する。
2. 古いメモリ領域からデータをバイト単位でコピーする。
3. 古いメモリ領域を解放する。
これをループ内で頻発させればどうなるか。メモリの断片化(ヒープのフラグメンテーション)が加速度的に進行する。数万件のデータを処理する際、1件ずつ `Preserve` を実行するのは、システム全体に対する自傷行為に他ならない。
—
2. 賢者の実装:バッファ・オーバーアロケーション戦略
メモリのコピーコストを最小化する唯一の解は、「拡張の回数を劇的に減らすこと」だ。配列を一度の拡張で使い切るのではなく、倍々(あるいは一定の塊)で確保する。
以下のコードは、数万件のレコードを処理する際の標準的な最適化パターンである。
‘ メモリ効率を意識した動的配列拡張のベストプラクティス
Public Sub ProcessLargeData()
Dim dataArray() As String
Dim capacity As Long: capacity = 1024 ‘ 初期バッファサイズ
Dim count As Long: count = 0
ReDim dataArray(0 To capacity – 1)
‘ データ取得のループ(例:CSVやDBからの読み込み)
Do While Not Reader.EOF
‘ キャパシティを超過した瞬間にのみ拡張を行う(指数的成長)
If count >= capacity Then
capacity = capacity 2
ReDim Preserve dataArray(0 To capacity – 1)
End If
dataArray(count) = Reader.Value
count = count + 1
Loop
‘ 最後に、実際に使用したサイズまで削り込む
If count > 0 Then
ReDim Preserve dataArray(0 To count – 1)
Else
Erase dataArray ‘ メモリ解放
End If
End Sub
この実装であれば、10万件のデータに対しても、`ReDim` の実行回数はわずか7回程度に抑えられる。`Preserve` のコストを無視できるレベルまで減らすことが、パフォーマンス向上の真髄だ。
—
3. レガシー環境におけるメモリ管理の鉄則
Excel VBAを長く運用していると、メモリリークの温床に悩まされることが多い。特に `ADODB.Recordset` や `FileSystemObject` を扱う際は、オブジェクトを明示的に解放しなければならない。
配列を扱う際も同様だ。大きな配列を使い終えた後は、`Erase` を用いて明示的にメモリを解放する習慣をつけよ。
‘ オブジェクトと配列のライフサイクル管理
Public Sub SafeProcess()
Dim rs As Object
Dim results() As Variant
On Error GoTo Cleanup
‘ 処理ロジック…
Cleanup:
‘ 終了処理を確実に実行する
If Not rs Is Nothing Then
If rs.State = 1 Then rs.Close
Set rs = Nothing
End If
‘ 配列の破棄(特にグローバル変数や静的配列の場合、必須)
Erase results
End Sub
—
4. 究極の選択:配列か、それとも Collection か?
もし、あなたが「配列の拡張が面倒だ」と感じているなら、`Collection` や `Scripting.Dictionary` を使う手もある。これらは内部でリンクドリスト構造等を採用しており、メモリ管理をランタイムに委譲できる。
しかし、純粋な計算速度とメモリ占有量においては、適切に管理された「配列」に勝るものはない。
- Dictionary: 柔軟だが、オーバーヘッドが大きく、メモリを食う。
- 配列: 厳格だが、最速であり、メモリ消費を理論値まで絞り込める。
システム間連携や大量のデータ変換(ETL)を行うシステムにおいて、我々エンジニアが選ぶべきは常に後者である。
—
アーキテクトからの提言
VBAで「動かない」「遅い」と嘆く者の多くは、言語の仕様を理解せず、ただコードを書き散らしているだけだ。配列一つとっても、メモリの確保順序や解放タイミングを意識するだけで、処理速度は数倍から数十倍に跳ね上がる。
コードは、ただ動けばいいのではない。「メモリの呼吸」を感じ、CPUの負担を最小化すること。 それこそが、レガシーシステムを支える真のエンジニアの矜持だ。
明日からの開発において、あなたの書く `ReDim` が、単なる記述ではなく「計算された最適化」であることを期待する。
