今日は数は少ないですがおもしろいコミットがあったのでちょっと遊んでみました。
nobu:r29518 2010-10-17 10:40:22 +0900
こちらも次の r29519 に絡んで発覚したものと思われますが、ネストした Module や Class を定義した時に rb_gc_register_mark_object を呼んでそれを GC のルートに追加しています。テストを見ると、
assert_normal_exit('File.__send__(:remove_const, :Stat); at_exit{File.stat(".")}; GC.start')
こんな感じなので、「C で実装されているメソッドからは定数を(VALUE 型の変数で直接)アクセスできてしまうけど、remove_const で消されていると GC で回収されていて SEGV」みたいな状況なのだろうと思います。
無名モジュールに module_eval 等でクラスやモジュールを定義した時に GC 対象になるのかちょと気になったので確認してみたのですが、rb_define_class_id_under 等は組み込みや拡張ライブラリのクラス/モジュールを定義する時に使われる C の API で Ruby からクラスやモジュールを定義してもここは通らないみたいでした。なのでこんなことしても大丈夫です。
100000.times do mod = Module.new mod.module_eval "class A; end" end
nobu:r29519 2010-10-17 10:47:15 +0900
[Bug #3769] で報告されているように Module#remove_const のドキュメントには組み込みのクラスは削除できないと書かれていますが実際には削除できてしまうので、ドキュメントのほうを変更しています。もちろんそんなことして何がおきても知らんよ、という警告つきです。「Feel Free to Shoot Your Own Foot.」
irb でちょっと試しに String とか Array を消してみたら当然すぐエラー終了してしまうんですけど、NilClass を削除してみたらこれが意外にそこそこ動くんですよね。定数削除されてもクラスオブジェクト自体は消えないのでメソッドは呼べるし、NilClass なんてめったに直接参照しないからですね。