Perlのガーベッジコレクション(GC)の特性について



  1. Perl




  2. 読み物


 オブジェクト(変数や値)が必要ではなくなったときに、自動的にメモリ上から解放する仕組みをガーベッジコレクションといいます。ガーベッジというのは、ごみという意味。コレクションというのは集めるという意味です。


Perlガーベッジコレクションは参照カウント方式

 Perlガーベッジコレクション参照カウント方式と呼ばれるものです。オブジェクトへの参照が作成されるたびに、参照カウントをひとつ増やします。反対に、オブジェクトの参照が取り除かれたときに、参照カウントをひとつ減らします。参照カウントが0になったタイミングで、そのオブジェクトは解放されます。

 一般論でいえば、参照カウント方式は、実装が単純で高速です。これはPerlのパフォーマンス上のよい特性だといえます。ガーベッジコレクションで、パフォーマンスの問題で悩むという事例をPerlでは聞いたことがないです(今のところ)。

 次に、オブジェクトが破棄されるタイミングは、参照がなくなったタイミングなので、プログラマーが、オブジェクトの破棄のタイミングを正確に知ることができます。

 たとえばJavaでは、ガーベッジコレクションが世代別CGと呼ばれ、後に解放されるオブジェクトが存在するために、すぐに実行したい後処理をfinalizeに書くことはできません。Perlの場合は、リファレンスカウント方式ですので、デストラクタDESTROYに、処理を記述すれば、参照がなくなったタイミングで処理を実行することができます。

 二番目の特徴は省メモリです。参照がなくなったタイミングで処理がされるので、メモリ上にオブジェクトが残りません。現代ではメモリというのはたくさんつむことができるようになったので、この欠点は昔ほどは、目立たなくなりましたが、メモリ食いなプログラムは、小さなマシンでは、スワップイン、スワップアウトが起こる原因にもなるので、省メモリなほうが、管理しやすいといえるでしょう。

 三つ目は、世代別GCのように、FullGCのタイミングで停止しないことです。世代別GCでは、特定のタイミングで、多くのオブジェクトが解放されるFullGCと呼ばれるものがあります。FullGCの最中は、プログラムがストップします。でもWebアプリなどでは、それほど気にすることはないでしょう(たぶん)。

参照カウント方式の欠点は相互参照を解決できないこと

 参照カウント方式の明らかな欠点は、相互参照を解決できないことです。相互に参照されているオブジェクトは、メモリ上から解放されません。オブジェクトが相互参照、または循環参照している場合は、プログラマーが気をつける必要があります。

# 相互参照
my $hash1 = {};
my $hash2 = {};
$hash1->{hash2} = $hash2;
$hash2->{hash1} = $hash1;

 上記のように記述するだけで、相互参照が発生して、このオブジェクトは自動的には解放されません。これは、プログラマーが注意をしていないと、容易に起こります。ですので、相互参照している場合は、プログラマーが自分で、どちらかのオブジェクトの参照を、参照カウントを増やさない、ウィークリファレンスに変更してあげる必要があります。

use Scalar::Util 'weaken';

# 相互参照
my $hash1 = {};
my $hash2 = {};
$hash1->{hash2} = $hash2;
$hash2->{hash1} = $hash1;


# 一方を弱い参照にする
weaken $hash1->{hash2};

 このようにすると、$hash1か$hash2の参照が0になった時点で、メモリは解放されます。

 このようにPerlでは、ガーベッジコレクションの実装は、参照カウント方式で、パフォーマンス上の問題に悩まされることが、ほとんどない代わりに、相互参照について、自分で意識しなければならないということを、覚えておきましょう。