camel

以下に触発されて。

パッケージの階層はどこまで深く出来るか

それを調べるために、以下のスクリプトを用意した。

#!/usr/local/bin/perl
use strict;
use warnings;
use Data::Dumper;
my $maxdepth = shift || 1024;
my $ns = 'P';
sub dummy { 1 }
print Dumper \%P::;

for my $i ( 1 .. $maxdepth ) {
    no strict 'refs';
    local *{ $ns . '::dummy' } = \&dummy;
    printf "depth=%d; P::...::dummy=%d\r", $i, &{ $ns . '::dummy' };
    $ns .= '::P';
}
print "\n";
print Dumper \%P::;
eval { 
    no strict 'refs'; 
    warn &{ 'P' . '::' . 'dummy' };
};
warn $@ if $@;
system qw/ps ux -p/, $$;

これを使って調べてみると、答えは「メモリーの持つ限りどこまでも」でOKのようだ。20000ぐらいまでやってみて、使用メモリーが600MBを超えたのでそこで怖じ気づいてそこから先は試していない。

Perlの名前空間(namespace)はどのように実装されているか

ここからが本題。

上のscriptをもう一度よく見てみると、なぜこれほどメモリーを食うのか少し困惑するのではないか。やっている事は名前空間を作って、そこに一時的にサブルーチンを登録しているだけである。それなのに使用メモリーはどんどん増えて行く。なぜだろう。

そこで注目していただきたいのが、Dumperの出力。それだけに注目すると、こうなる。

ループ前
VAR1 = {};
ループ後
depth=1024; P::...::dummy=1
$VAR1 = {
          'P::' => *{'P::P::'},
          'dummy' => *P::dummy
        };

当初は空っぽだった%P::に何やら入っている。でも%P::ってなんだろ?

これが、Symbol Table Hash, 略して stash だ。一目見ての通り、Stash は名前空間中の変数をキーとする hash になっている。そしてその中のP::というキーに対応する値が、次の名前空間へ参照となっている。別の言い方をすると、$P::P::P::varに一度でもアクセスすると、%P::P::P::というstashだけではなく、%P::P::%P::も生成されるのだ。

% perl -MData::Dumper -e '$P::P::P::var=1; print Dumper \%P::'
$VAR1 = {
          'P::' => *{'P::P::'}
        };

そして、ここが重要なのだが、一端生成された stash は、プロセスが死ぬまで解放されない。stash というのは関数定義まで含めたグローバル変数の置き場所なのだからある意味当然ではある。

これもまた、パッケージ変数をも含めたグローバル変数をむやみに使うべきではない理由なのである。

この名前空間と stash に関しては、「ヒョウ本」こと「実用Perlプログラミング」に詳しい。「モダンPerl入門」の方がむしろ「実用」にふさわしい本であるが、原題の"Advanced Perl Programming"のAdvancedなところに関しては今でもこれが書籍としては最もAdvancedな話題を扱っていると思う。

Dan the Perl Monger