Set object = Nothingを書かないとメモリがリリースされない

JVLinkを操作するクラスと関連するクラスを作って、JVDataのレコードをテキストファイルに出力するルーチンを実行したら、使用したメモリがリリースされずにメモリ消費量が増え続けてしまうエラーを見つけました。

VBAで独自のクラスモジュールやScripting.FileSystemObjectなどのライブラリのオブジェクトを使うときは次のようなコードを書きます。

Sub Something()
  Dim object1 as Class1    ' オブジェクト宣言
  Set object1 = New Class1 ' オブジェクト生成
  ' 何かしらの処理
  Set object1 = Nothing    ' オブジェクト消滅
End Sub

調べていたら、Set object = Nothingを書いていないとメモリがリリースされないことがわかりました。書かなくてもいいと思って書いていませんでした。メモリ消費が大きそうなJVDataのレコードを蓄積するオブジェクトをSet object = Nothingで消去するようにしたら、メモリ消費量が上がったり下がったりしたので間違いないと思います。メモリ消費量の変化はタスクマネージャで確認しました。

オブジェクトを生成したらSet object = Nothingをちゃんと書いてオブジェクトを消去して、JVDataのレコードを取得、蓄積、データベース保存のルーチンでメモリ消費量が一定になるように直しています。全てのコードでNothingを書いていなかったからちょっと面倒です。

Set object = Nothingを書かなくても、End Subなどでプロシージャが終了すればクラスモジュールのClass_Terminateプロシージャが自動で実行されるので、そこでメモリリリースもされるのだと誤解していました。プロシージャ内でオブジェクトを宣言していて、プロシージャが終了すれば自動でオブジェクトが使用したメモリがリリースされるから、Nothingを書かなくてもいいなと思っていたんです。失敗しました。

このNothingを書いていない話と関連して、オブジェクトの寿命が不明瞭なプログラムを作っていたのでこれも見直そうと思います。私のポストSQLiteデータベースに接続するSQLiteDatabaseConnectionクラスでは、オブジェクト生成時にデータベース接続オープンを自動でして、オブジェクト消滅時に、接続クローズをしているのですが、これは良くない設計だと思っています。このデータベースクラスの使用側でオブジェクトの生成・消滅をきちんと管理すれば問題ないと思うのですが、このクラスのオブジェクトを使用するプロシージャが多数あると、データベースの接続オープンが多数行われてエラーになるからです。こういうオブジェクトの独立が弱いと、プログラムを作っている私の頭も混乱するし、エラーの温床になりますよね[1] [2]

2017-08-07 VBAのガベージコレクションについて補足

4.1.5 オブジェクト消滅の管理 - EXCEL-VBA開発講座にVBAのメモリ管理の仕組みについて書いてあって読みました。オブジェクトを生成すると、VBAはそのオブジェクトを参照している数をカウントしていて、Set object = Nothingはカウントを1つ減らし、参照数が0になるとメモリをリリースするとのことです。


  1. CODE COMPLETE 第2版 上 完全なプログラミングを目指して (2005年 スティーブ・マコネル)に書いてありました。 ↩︎

  2. プリンシプル オブ プログラミング 3年目までに身につけたい一生役立つ101の原理原則(2016年 上田勲)に書いてありました。 ↩︎

Kosuke Maeda / まえだこうすけ

「機械学習で競馬予想して勝てるのか?」をテーマに活動中! QiitaにはR、VBAなどのTipsを投稿しています。