Perlでは、匿名サブルーチンとクロージャがサポートされています。匿名サブルーチンは、名前なしでsubで宣言され、コードへのリファレンスを返します。クロージャとは、作成時に存在していた変数を含むレキシカルスコープを「キャッチ」するサブルーチンです。
匿名サブルーチンとクロージャの例:
sub make_incrementer { my $inc = shift; return sub { $_[0] + $inc }; } my $inc10 = make_incrementer(10); print $inc10->(5); # 15と表示されます
クロージャは、ファンクションファクトリー、ジェネレーター、コールバック(例えば、map/grepやイベントハンドラーで)の作成に活発に使用されます。
重要な点: クロージャが、直接または構造を介して自身を指す変数を参照している場合、循環参照が発生し、メモリリークが起こる可能性があります。
クロージャに閉じ込められた変数は、いつ解放されますか?
myとourで挙動に違いはありますか?
回答: クロージャ内に閉じ込められたmy変数は、クロージャへのリファレンスが少なくとも1つ存在する間は生き続けます。関数が完了しても解放されず、その寿命はクロージャの寿命と同じです。our変数にはこのような挙動はなく、すべてのクロージャからアクセス可能で、プログラムの終了時に解放されます。クロージャを削除するのを忘れると、メモリリークが発生する可能性があります。
問題の例:
my $cref; { my $val = 5; $cref = sub { $val++ }; } # $valは$crefが生存している限り存在します
物語
サーバーアプリケーションでは、各ユーザーにコールバックのためのクロージャコンテキストが作成されていました。しかし、クロージャもユーザー構造を参照しており、循環参照が発生しました。このため、ガーベジコレクションはログアウト後もユーザーオブジェクトをクリーンアップできず、メモリリークが指数関数的に増加しました。
物語
バックグラウンドイベント処理のデーモンアプリケーションでは、クロージャに閉じ込められたカウンター変数が使用されましたが、それらが参照していた配列のクリアを忘れていました。その結果、数個の忘れられたクロージャのせいで古いメッセージデータが偶然溜まり、デーモンがクラッシュするまで手動でヒープをクリーンアップする必要がありました。
物語
開発者はクロージャ内で
our変数を使用し、「閉じ込められた」挙動を期待しましたが、すべてのクロージャが1つの変数を共有し、並行実行時にデータ競合が発生しました。このバグは、異なるパラメータを持つユーザーが同時に作業したときに現れました(誤った計算)。