ENTRANCE|INFORMATION | DIARY | LABORATORY | LINK
CAGE(一般掲示板)|Developper's Nest(開発掲示板)|こころ宙
ここでは、Ruby の拡張モジュールを作成しているときに陥りやすい注意点について書いていきます。
拡張モジュール (Ruby 内部) において、配列オブジェクト Array
は VALUE
の配列を表しています。この Array
オブジェクト、配列を操作する Ruby API を使えば、要素が追加されたときなど必要に応じて配列のバッファを取り直してくれたり、並び替えることもできたり、自由長配列を簡単に扱うことができるようになります。さらに、VALUE
が 単なるポインタ であることも考えると、「実は自分で自由長配列を実装する必要なんかないんじゃないか?」という考えに行き着いてしまうかもしれません。(行き着きました(笑))
しかし、この Array
に Ruby オブジェクト以外のものを (キャストによって強引に) 格納すると、大抵、SEGV が発生してプログラムは落ちてしまいます。
なぜでしょうか?
原因は ガベージコレクタ にあります。
(現在のところ) Ruby のガベージコレクタは、Ruby が管理しているすべてのオブジェクトに対して マーク を行います。そして、マークされていないオブジェクトを「使われていないオブジェクト」と判断して、メモリから破棄していくのです。この作業で Array
オブジェクトにマークが行われると、Array
の中に格納されているすべてのオブジェクトにもマークが行われます。配列の中身が勝手に消え去ってしまったら困りますからね。
そして、この作業では、参照先のオブジェクトが Ruby オブジェクトであるかどうか、というチェックは行われません。「行えばいいじゃん!」と思うかもしれませんが、それだと余計なオーバヘッドがかかってしまい、パフォーマンスが低下してしまいます。というより、そもそも、Array
に Ruby オブジェクト以外のものが格納されることは想定されていないのですから、そんなチェックを行う理由はありません。
Ruby オブジェクト以外のものにマークが行われると、言ってみればメチャクチャな内容の Ruby オブジェクトに対してマークを行うことになるわけで、たいていの場合、そこでプログラムは落ちてしまいます。
というわけで、「ちょっとだから」などという理由で Array
オブジェクトに Ruby オブジェクト以外のデータを格納することはやめましょう。わかりにくいバグの原因となってしまいます。
ENTRANCE|INFORMATION | DIARY | LABORATORY | LINK
CAGE(一般掲示板)|Developper's Nest(開発掲示板)|こころ宙