(2010/11/22 追記: 5.12ではこの問題は解決され、SEGVはおきなくなっている。)
Released CvGVs could cause SEGVの注で「シンボルテーブル <-> GV間にも循環参照があるため同じ問題を抱えるのだが,こちらは真のウィークリファレンスを導入して解決した」と書いたが、よく調べてみると解決しているとは言いがたい状態だった。
まず、スタッシュ(Symbol Table Hash)とGV(type glob)の関係だが、スタッシュはGVを「所有」している*1。一方、GVはフィールドに所属しているスタッシュを持ち、CレベルではGvSTASH(gv)として参照できるのだが、このGVからスタッシュへの関係はウィークリファレンスとなっている。したがって、不要なGVやスタッシュは適切に解放される。しかし、このデータ構造は、GVが存在しつつ所属元のスタッシュが解放されるという事が起こり得る*2。
以下のスクリプトはそのような状況を作り出す。
#!perl use strict; use Devel::Peek; my $gv = *Foo::bar; delete $::{'Foo::'}; # 所属元のスタッシュを削除する Dump $gv; __END__
結果:
SV = PVGV(0x1004de68) at 0x1004f668
REFCNT = 1
FLAGS = (PADMY,FAKE,MULTI,IMPORTALL)
NAME = "bar"
NAMELEN = 3
GvSTASH = 0x0
GP = 0x10067b50
SV = 0x0
REFCNT = 2
IO = 0x0
FORM = 0x0
AV = 0x0
HV = 0x0
CV = 0x0
CVGEN = 0x0
LINE = 6
FILE = "stash.pl"
FLAGS = 0xf2
EGV = 0x1004f710 "bar"このように GvSTASH = 0x0 となっており、ウィークリファレンスメカニズムが適切に働いていることが分かる。
しかし、このGVはデータとしては壊れており、何か操作を加えるとすぐSEGVする。
#!perl use strict; use Devel::Peek; my $gv = *Foo::bar; delete $::{'Foo::'}; print $gv; # SEGV Dump($gv); __END__
これはGvSTASHを参照している箇所でNULLかどうかをチェックすればよいので、問題の解決はそれほど困難でなないと思われる。
よって[あとで報告する]。