Perlでは, ガベージコレクション(GC)について「リファレンスカウント方式(参照カウント方式)」という仕組みを利用しています. リファレンスカウント方式は, 全てのデータに対して「参照カウント」と呼ばれる整数値を隠しパラメータを持たせ, そのデータが参照されている数を格納し, これが0になった時にデータを破棄する... というGCの仕組みです.
この記事では, PerlのGCとその根幹である「リファレンスカウント」について, 本当にざっくり説明していきます.
リファレンスカウントを確認する
ある変数に紐付いているデータ(つまりは, 変数の中身)のリファレンスカウントは, Devel::Peekモジュールを利用すれば確認できます.
use strict; use warnings; use Devel::Peek; my $foo = 1; # # (1) Dump $foo; # my $bar = \$foo; # # (2) Dump $foo; # undef $bar; # # (3) Dump $foo; #
このコードを実行すると, 結果は次のようになります.
$ perl refcnt.pl SV = IV(0x7fe27b009968) at 0x7fe27b009978 # (1)の結果 REFCNT = 1 FLAGS = (PADMY,IOK,pIOK) IV = 1 SV = IV(0x7fe27b009968) at 0x7fe27b009978 # (2)の結果 REFCNT = 2 FLAGS = (PADMY,IOK,pIOK) IV = 1 SV = IV(0x7fe27b009968) at 0x7fe27b009978 # (3)の結果 REFCNT = 1 FLAGS = (PADMY,IOK,pIOK) IV = 1
Devel::Peekを利用すると, このようにPerlの変数の内部構造を見ることが出来ます(この辺りの知識は, XSモジュール(C言語を利用したPerlモジュール)の開発をしないのであれば, そこまでしっかり理解しに行く必要はほとんどないです). この中の「REFCNT」というのが, その変数に格納されているデータのリファレンスカウントを示しています.
...詳しく見て行きましょう.
まず(1)ですが, $foo
という変数を宣言し, その中に1というデータを格納しました.
そのため, このときの$foo
が格納しているデータへのREFCNTは1です($foo
が参照している).
次に(2)では, $foo
というスカラ変数のリファレンスを$bar
に代入しています.
これによって, $foo
という変数に格納されているデータは, リファレンスを通して$bar
からも参照できるようになりました.
そのため, $foo
が格納しているデータへのREFCNTは2に増えています.
最後に(3)では, $bar
という変数に格納された$foo
へのリファレンスを, undef
関数を利用してundef
に置き換えています.
そのため, $foo
という変数に格納されているデータへの参照は, 1つ減ってしまったので, REFCNTは1になっている訳です.
リファレンスカウントとスコープ
さて, コードを少し書き換えてみます.
先程のコードの(2)にあたる部分を, スコープ({
と}
の間)内に記述するようにしました.
use strict; use warnings; use Devel::Peek; my $foo = 1; { my $bar = \$foo; Dump $foo; # (1) } # ※ Dump $foo; # (2)
実行結果は次の通りです.
SV = IV(0x7fd3a4802f68) at 0x7fd3a4802f78 # (1) REFCNT = 2 FLAGS = (PADMY,IOK,pIOK) IV = 1 SV = IV(0x7fd3a4802f68) at 0x7fd3a4802f78 # (2) REFCNT = 1 FLAGS = (PADMY,IOK,pIOK) IV = 1
先程の例と同じく, (1)では$foo
による参照と, そこへのリファレンスを持った$bar
による参照があるので, $foo
に格納されたデータのREFCNTは2です.
一方, 次の(2)では, REFCNTは1に減ってしまっています.
これは, $bar
がスコープを抜けた段階(※の部分)で廃棄され, これによって$bar
が持っているデータ($foo
へのリファレンス)のREFCNTが0になって破棄され, 結果として$foo
に格納されているデータへの参照が$foo
という変数からの参照のみになってしまったので, REFCNTが1減っている訳です.
よく, Perlの解説で「スコープを抜けた時点で, 変数は破棄されます」という説明がありますが, その際にPerlの内部ではこのような処理が行われ, 変数とそれに紐づくデータが破棄されている訳です.
まとめ
Perlのガベージコレクションと, その根幹を担う「リファレンスカウント」という仕組みを紹介しました. Qiita:Teamにサクっと書いたテキストをサルベージしたものなので, 文中に間違い等々あるかもしれません. その際はぜひご指摘いただければ嬉しいです...!