- 結論
- DatabaseやRecordsetオブジェクトでは "Nothing"キーワード の使用はメモリ消費の観点からは無意味。同じデータベースに対して繰り返しアクセスする場合には "dbs.Close" さえも省略してもメモリには影響しない。また、Setステートメントで宣言されたオブジェクトは適用範囲外になると自動的に解放され、特にRecordSetオブジェクトではCloseメソッドを省略してもそのメモリ領域は解放される。
VBAのプログラムにおいて、DatabaseやRecordsetオブジェクト、ActiveXなどのライブラリ化されたオブジェクト(クラス)、あるいはExcelやWordのようなオブジェクトを扱う際には、まず「Set ステートメント」によってそれらのオブジェクトへの参照を変数として宣言し、以降のコードではその変数に対してプロパティを操作したり、メソッドを実行したりします。特に、この中の DatabaseやRecordsetオブジェクト については、Accessでは定型的とも言えるほどしばしば使われるオブジェクトではないでしょうか?。
一般に、メモリなどのリソース消費の観点から、『Setステートメントによって参照を宣言したオブジェクトは、最後に「Nothing キーワード」によってオブジェクトを解放する』ことが勧められているようですが、Accessのヘルプの"使用例"のコードを見ると、Setステートメントはもちろん必ずあるものの、Nothing キーワードは必ずしも使われていないようです。そこで、この Nothing キーワードの必要性について、それを使った場合と使わなかった場合とでメモリの使用状況がどう変わるか調べてみることにしました。
テストでは次のようなプロシージャを作成し実行してみました。プログラムとしてはデータベースとレコードセットを開いて何もしないで閉じるという非常に単純なものです。このコードの中で灰色で示された行はその時点での物理メモリの残容量をデバッグウィンドウに表示させる部分です。「GetMemSize」というFunctionプロシージャはこのテストのために独自に作ったもので、GlobalMemoryStatus というWindowsAPI関数を使ってメモリの状況を取得し物理メモリの残容量を返り値として返します(このコードについては特に説明しません)。
Sub MemoryTest()
Dim dbs As Database
Dim rst As Recordset
Debug.Print GetMemSize & " Byte"・・・・・・<1>
Set dbs = CurrentDb
Debug.Print GetMemSize & " Byte"・・・・・・<2>
Set rst = dbs.OpenRecordset("tblFieldTest")
Debug.Print GetMemSize & " Byte"・・・・・・<3>
rst.Close
Debug.Print GetMemSize & " Byte"・・・・・・<4>
Set rst = Nothing
Debug.Print GetMemSize & " Byte"・・・・・・<5>
dbs.Close
Debug.Print GetMemSize & " Byte"・・・・・・<6>
Set dbs = Nothing
Debug.Print GetMemSize & " Byte"・・・・・・<7>
End Sub
デバッグウィンドウに表示されたテスト結果は次のようなものです。ディスクキャッシュなどの影響を避けるためにパソコンをリブートしその直後にテストコードを実行した結果と、そのまますぐに再実行した結果です。
|
1回目 |
2回目 |
<1> |
9314304 バイト |
9011200 バイト |
<2> |
9064448 バイト |
9011200 バイト |
<3> |
9039872 バイト |
8998912 バイト |
<4> |
9052160 バイト |
9011200 バイト |
<5> |
9052160 バイト |
9011200 バイト |
<6> |
9048064 バイト |
9011200 バイト |
<7> |
9048064 バイト |
9011200 バイト |
リブート直後の1回目では、<1>、<2>、<3>と徐々にメモリが減っていくのが分かります。DatabaseやRecordsetの変数にオブジェクトが代入されることによってメモリが消費されていくことを示しています。特にDatabaseオブジェクトの消費量は大きいようです。そしてRecordsetを閉じることによってある程度のメモリが解放されます。しかし、その次の"Set rst = Nothing"ではメモリに何の変化も現れません(<4>=<5>)。また、"Set dbs = Nothing"の場合にもそれを実行するのとしないのとではまったく違いは出ていません(<6>=<7>)。このことから、「DatabaseやRecordsetオブジェクトでは"Nothing"キーワードの使用はメモリ消費の観点からは無意味」と言えます。なお、"dbs.Close"によってなぜメモリが減ってしまうかについては原因は不明です。
一方、2回目のテスト結果はどうでしょう?。"Set rst = dbs.OpenRecordset"によってメモリが減って、"rst.Close"によってメモリが元の状態に戻っていることは明らかですが、不思議なことに"Set rst = Nothing"も、"dbs.Close"も、"Set dbs = Nothing"も、まったくメモリには影響を与えていません。これがDAOのキャッシュの効果なのかどうかは分かりませんが、「同じデータベース(普通はCurrentDbでしょう)に対して繰り返しアクセスする場合、"dbs.Close"さえも省略してもメモリの残容量には影響しない」と言えそうです。
一方、Setステートメントで宣言されたオブジェクトは適用範囲外になると自動的に解放されます(とヘルプにはあります)。これについても確認してみました。上記のテスト用プロシージャ"MemoryTest"の中の"rst.Close"の行の前に "Exit Sub"を挿入、データベースとレコードセットを開いたままでプロシージャを終了するようにし、他のプロシージャにおいてこのプロシージャを呼び出す前と呼び出した後のメモリ残容量を比較してみました。結果は次の通りです。
|
1回目 |
2回目 |
呼び出し前 |
9162752 バイト |
7733248 バイト |
プロシージャ内 |
<1> |
9101312 バイト |
7733248 バイト |
<2> |
8863744 バイト |
7733248 バイト |
<3> |
8839168 バイト |
7720960 バイト |
呼び出し後 |
8847360 バイト |
7733248 バイト |
"rst.Close"が実行されていないにも関わらずプロシージャ内の<3>よりも呼び出し後の方がメモリ容量が多くなっています。このことから、確かに「オブジェクトは適用範囲外になると自動的に解放されている」ことが分かります。ここでどの程度メモリ容量が復元されたかを比較してみます。
- 前回のテストの<7>-<3> = 9048064Byte-9039872Byte = 8192Byte
- このテストの呼び出し後-プロシージャ内<3>= 8847360Byte-8839168Byte=8192Byte
と、まったく同じ数値となっています。このことは、RecordSetオブジェクトは適用範囲外になるとCloseされたのと同じ状態に解放されるということを示しています。なお、2回目では呼び出し前も呼び出し後も同じメモリ残量となっていますが、これは前に行ったテストと同じ要因と考えられます。 |