UBound関数やLBound関数に配列を返す関数を渡すとメモリリークする
VBAはあまりメモリリークが発生することはないのですが、それでも発生する場合があります。
Microsoftのサポートページには、
「UBound 関数または LBound 関数のパラメータとして配列を返す関数を使用すると、配列のために確保されたメモリが解放されません。」
とあります。
メモリリークとは、本来であれば関数終了時に解放されるメモリ領域が解放されずに残ってしまうことを言います。解放されないため他のアプリケーションなどが利用できず、利用できるメモリ容量が減ることになります。
対応方法は通常はPCの再起動しかありません。
VBAに限りませんがメモリリークの問題で一番やっかいなのが、メモリーリークを発生させているプログラム自体は正しく機能しているため、メモリリークしていることを監視していないと問題に気が付かない点です。メモリリークのチェックツールにはLinuxやMacであればC/C++言語用のValgrindなどがありますが、VBAには無いためコーディング時に気を付けるしかありません。
メモリリークする書き方
配列を返す関数の代表的なものがSplit関数です。
実際にはSplit関数などの配列をそのままUBound関数に渡すようなコードを書くと、Split関数の結果である配列自体を利用できないため、こういうコードを書くことはほとんどないとは思います。
ただ、要素数だけを取りたい場合などでそのようなコードを以下のように書いてしまうとメモリリークします。
1 2 3 4 5 6 7 8 9 10 11 |
Sub UBoundSplitMemoryLeak() Dim v Dim s Dim i s = "1,2,3" For i = 0 To UBound(Split(s, ",")) Debug.Print Split(s, ",")(i) Next End Sub |
メモリリークしない書き方
メモリリークしない書き方は、要素数だけが欲しい場合でも、関数が返却する配列は一度変数に代入してから使うように書きます。
可能であればSplit関数はFor Each構文で処理しましょう。要素数が関係なく、配列の先頭から最後までを処理してくれます。
1 2 3 4 5 6 7 8 9 10 |
Sub UBoundSplitTest() Dim v Dim s v = Split("1,2,3", ",") For Each s In v Debug.Print s Next End Sub |
次に要素数を取得する場合の書き方です。以下のように必ず要素数格納用の変数に代入してから使うようにしましょう。
1 2 3 4 5 6 7 8 9 10 11 12 |
Sub UBoundSplitTest2() Dim v Dim cnt Dim i v = Split("1,2,3", ",") cnt = UBound(v) For i = 0 To cnt Debug.Print v(i) Next End Sub |