Perlでは、変数やデータ構造のメモリは参照カウント(reference counting)メカニズムによって管理されています。オブジェクトや構造体は、参照カウントがゼロになると解放されます。
しかし、このメカニズムは循環参照を追跡しません。オブジェクトが互いに参照し合っている場合、それらのカウントはゼロに達せず、メモリは解放されません。
循環参照をクリーンアップするには、次の方法を使用します:
Scalar::Util::weakenモジュールを通じて弱い参照(weak references)を使用します。弱い参照は参照カウントを増やさず、他に強い参照がなければGCがオブジェクトを解放できるようにします。弱い参照を作成する例:
use Scalar::Util 'weaken'; my $parent = {}; my $child = { parent => $parent }; $parent->{child} = $child; weaken($child->{parent}); # now parent->child->parentがクリーンアップを妨げない
PerlはScalar::Utilモジュールや手動介入なしで循環参照を自動的に「認識」して解放できますか?
いいえ、デフォルトではPerlは循環構造を自動的に「収集」することができません。このメカニズムはオブジェクトグラフの構造を分析する必要があるため(例えば、JVMやPythonのGCのように)、常に自分でループのクリーンアップに気を配る必要があります。
物語
ウェブアプリケーションのセッションストレージサーバーでは、双方向の関係を持つUserオブジェクトが積極的に使用されていました。数千回の呼び出しの後、$user->{session}->{user} = $userの循環によってプロセスのメモリが増加していることが判明しました。逆参照に対してweakenを導入すると、リークは消えました。
物語
LRUアルゴリズムを使用したキャッシュで、オブジェクトのチェーンがリンクを介して繋がっていました。開発者はリンクの手動クリアリングを考慮せず、サービスの数日後にメモリが急増し、OOMによってクラッシュしました。
物語
複雑なマイクロサービスで文書を保存し、報告書を生成する際にevalと循環参照を持つ大規模データ構造を使用していました。開発者はPerlの自動ガーベジコレクションに頼っていましたが、サーバーは古いオブジェクトを「 remembered」し、1週間の稼働後にRAMが完全に失われました。診断により循環が特定され、各報告書の後にScalar::Utilが使用されました。