Test::LeakTrace 0.07
Devel::LeakTraceもそうだが,0.07以前のTest::LeakTraceは不可解な値を報告することがあった。
$ perl -MDevel::LeakTrace::Fast -e 'my $a; my $b = \$a' leaked SV(0x0x8f87dd8) from -e line 1 leaked SV(0x0x8f87c94) from -e line 1 $ perl -MTest::LeakTrace::Script -le -e 'my $a; my $b = \$a' leaked SCALAR(0x9f4cdd8) from -e line 1. $
「Test::LeakTraceをCPANにあげた」の記事でも,目的の相互参照だけでなく,空のハッシュが報告されている。
長らくこれが何なのか分からなかったのだが,この現象のメカニズムをどうやら特定できたので,それを修正してTest::LeakTrace 0.07としてリリースした。
この現象はperlのSVアロケーションメカニズムに関係しているようだ。まず,my変数はスクリプトのコンパイル時にperlによってスクラッチパッド(PAD)と呼ばれる領域に割り当てられる:
sub foo{ my $bar; # $barは静的に割り当てられる }
つまり,foo()を呼び出しても新しくSVが確保されることはない*1。
一方,my変数のハードリファレンスを返すこともできる:
sub foo{ my $bar; return \$bar; }
先の説明によれば,$barは静的に割り当てられているはずだ。しかし実際には,foo()を実行するたびに別のSVへのリファレンスが得られるのは周知の通りである。これは$barが静的に割り当てられるという事実と矛盾するように見える。
これはどうやら,my変数への参照*2を作成する時点で新たにSVが作成され,そのmy変数が所属するサブルーチンが次に呼び出されたときに,その新しいSVが利用されるという仕組みによるらしい。
このようなSVはTest::LeakTraceでのメモリリークの定義,つまり「ある区間内で新たに作成され,その区間が終了しても開放されないまま残っているSV」に当てはまるので,今までは報告されていたのだった。
これを検出するには,Perl 5.10.0で導入されたSvPADSTALEが利用できる。これはPAD変数が未使用のときにつけられるフラグで,ある種の警告を発生させるためや,state変数の初期化状態を保存するために使われている。
問題はSvPADSTALEが5.8で利用できないことだったが,これはリークの疑いのあるSVが,PADのどこかに存在すればリークではないものとみなすということにして対応した。