「CurrentDb」を安易に使うな:Access VBAのメモリ管理とパフォーマンスを極める
Access開発において、多くのエンジニアが犯す最大の過ちは「データベースオブジェクトのライフサイクル」を軽視することだ。その象徴が `CurrentDb` の乱用である。
「`CurrentDb` を呼び出せば、いつでも現在のデータベースにアクセスできる」という認識は、小規模なツールなら問題ない。しかし、数万件のレコードを処理するループや、高頻度で実行されるAPI連携の裏側でこれを行うと、システムは確実に、そして静かに悲鳴を上げる。
今日は、プロフェッショナルとして生き残るために避けては通れない、`DAO.Database` オブジェクトの最適化について深掘りする。
—
1. なぜ「CurrentDb」はパフォーマンスを殺すのか
`CurrentDb` は関数である。これを呼び出すたびに、Accessは内部で以下のプロセスを繰り返している。
1. メタデータへの再アクセス: 現在開いているデータベースの接続状態を再確認し、DAO.Databaseオブジェクトを再生成する。
2. オブジェクトの構築コスト: 内部的に新しいDatabaseオブジェクトのインスタンスをメモリ上に展開し、参照を返す。
3. ガベージコレクションの負荷: 使い捨てられた無数のオブジェクトがメモリ上に散らばり、VBAのガベージコレクション(GC)を過剰に稼働させる。
ループの中で `CurrentDb` を叩くことは、毎回「データベースへのドアを鍵で開けて、用が済んだら閉める」という重労働を繰り返しているのと同じだ。このオーバーヘッドが、大規模処理での「謎の遅延」の正体である。
—
2. 実践:DAO.Databaseをキャッシュする設計手法
解決策はシンプルだ。「一度生成したインスタンスをモジュールレベルでキャッシュし、使い回す」こと。これがエンジニアリングの基本である。
推奨される実装パターン
‘ 標準モジュール(またはクラスモジュール)の先頭で定義
Private m_db As DAO.Database
”’
”’
Public Property Get CurrentDB_Cached() As DAO.Database
‘ まだインスタンスが存在しないか、閉じられている場合にのみ再生成する
If m_db Is Nothing Then
Set m_db = CurrentDb
Else
‘ 必要に応じて接続状態の生存確認(Errトラップでチェック)
On Error Resume Next
If Err.Number <> 0 Or m_db.Name = “” Then
Set m_db = CurrentDb
End If
On Error GoTo 0
End If
Set CurrentDB_Cached = m_db
End Property
”’
”’
Public Sub TerminateDB()
If Not m_db Is Nothing Then
m_db.Close
Set m_db = Nothing
End If
End Sub
この手法を採用するだけで、ループ処理におけるオブジェクト生成コストをゼロに近づけることができる。
—
3. シニアエンジニアが意識すべき「メモリ解放の作法」
Accessはマルチスレッドではないが、メモリ管理には極めて神経質になるべきだ。VBAの「参照カウント」がゼロにならない限り、オブジェクトはメモリに居座り続ける。
Windows APIによる「強制的なメモリの整理」
大規模なバッチ処理を行う際、時折 `Set obj = Nothing` だけではメモリが開放しきれないことがある。Windows APIの `SetProcessWorkingSetSize` を活用することで、OSレベルでワーキングセットを最適化し、Accessが占有するメモリを解放することが可能だ。
‘ API宣言
If VBA7 Then
Private Declare PtrSafe Function SetProcessWorkingSetSize Lib “kernel32” _
(ByVal hProcess As LongPtr, ByVal dwMinimumWorkingSetSize As LongPtr, _
ByVal dwMaximumWorkingSetSize As LongPtr) As Long
Private Declare PtrSafe Function GetCurrentProcess Lib “kernel32” () As LongPtr
End If
‘ メモリ最適化用プロシージャ
Public Sub OptimizeMemory()
Call SetProcessWorkingSetSize(GetCurrentProcess(), -1, -1)
End Sub
これを長時間のデータ移行処理の合間に挟むだけで、OS側のスワップ発生を防ぎ、システム全体の安定性が劇的に向上する。
—
4. 結び:アーキテクトとしての矜持
なぜ「たかがAccess」でここまで拘るのか。それは、技術者がコードの深淵を理解していないシステムは、必ずどこかで「保守不能」という名の負債に陥るからだ。
- CurrentDbをキャッシュする:これは単なる高速化ではない、リソースへの敬意である。
- 明示的にオブジェクトを破棄する:これは次世代の保守担当者へのマナーである。
あなたの書く1行のVBAが、数年後の誰かを救うか、あるいは誰かを苦しめるか。その境界線は、こうした些細な設計思想の差に宿る。
「動けばいい」という段階は卒業したはずだ。次は、システムが呼吸するようにスムーズに動く、その裏側の設計に心血を注いでほしい。
