1. ホーム
  2. スクリプト・コラム
  3. パール

Perl6 のガベージコレクション

2022-01-03 04:12:51

参考カウント

Perl 5の参照カウントで。Perl5で何かを作成した場合、参照カウントが1以上になり、それが生かされるようになります。最も単純なケースでは、次のようになります。

# reference count of $a = 1, because lives in lexical pad
# lexical pad is gone, reference count to 0

Perl 5 では、値がオブジェクトの場合、DESTROY メソッドが呼び出されます。

Foo->
# $a->DESTROY called

外部リソースが関与しない場合、適時破壊は、プログラムが使用するメモリを管理するための単なる方法の1つです。プログラマーとしては、いつ、どのようにリサイクルされるかを気にする必要はありません。とはいえ、データベースハンドル(通常、データベースサーバーから限られた数しか提供されない)のような外部リソースを扱う必要がある場合、ジャストインタイムの破棄は非常に素晴らしい機能である。参照カウントはこれを提供することができる。

しかし、参照カウントにはいくつかの欠点がある。それは、perl 5のコア開発者を使っていることです。参照カウントを正しく動作させるのに何年もかかる。Xsで作業している場合、メモリリークや早期破損を防ぐために、常に参照カウントを知る必要がある。

マルチスレッド環境では、複数のスレッドからの参照の更新を同時に失いたくないので、同期を保つことが難しくなります(メモリリークや外部リソースが解放されないことにつながる可能性があるため)。これを避けるには、ある種のロックかアトミックな更新が必要ですが、どちらも安価ではありません。

  • Perl 5 ithread は C のようなプログラミング言語におけるスレッドというよりも、インタプリタ間で非共有メモリを持つメモリフォークに近いので、参照カウントのロックは必要ないことに注意してください。

参照カウントにはもうひとつ基本的な欠点があり、2つのオブジェクトが互いに参照を含んでいる場合、互いの参照カウントを0以上に保つ(循環参照)ため、決して破壊されることはない。実際には、A -> B -> C -> A のように、A, B, C がお互いを生かし続けるような、より深い関係になる傾向があります。

aの弱参照という概念は、Perl5でこのようなケースを回避するために開発されたものです。これは循環参照問題を解決することはできますが、そもそも循環参照(とルックアップ)問題を解決するのではなく、パフォーマンスに影響を与えるものです。弱参照はどこで使うのがベストなのかを知っておく必要があります。そうでなければ、不必要に早い段階でオブジェクトが破壊されてしまうかもしれません。

到達性解析

Perl 6 はそのコアにマルチスレッドがあるため、参照カウントはパフォーマンスとメンテナンスの面で問題があると早い段階で判断されました。その代わりに、より多くのメモリが必要となり、オブジェクトを安全に削除できるようになったときに、オブジェクトはメモリから追い出されます。

Perl6でもPerl5と同じようにDESTROYメソッドを作ることはできる、はずだ。 しかし、あなたは。それがいつ(もしあるとすれば)呼ばれるかを決定することはできない。
入らないことについて詳しすぎる、Perl 6のオブジェクトは、ガベージコレクションの実行が開始されたとき、例えば、一定のメモリ制限に達したときのみ破棄されます。その時だけ、メモリ内の他のオブジェクトがオブジェクトにアクセスできなくなり、そのオブジェクトにDESTROYメソッドがある場合、オブジェクトが削除される前にそれが呼び出されるのでしょうか?

Perl6では、プログラム終了時にガベージコレクションを行いません。適用可能なフェーザ(LEAVEやENDなど)はGETコールされなければなりませんが、フェーズのプログラムで実行されているコードによって(間接的に)開始される以外にガベージコレクションは行われません。

プログラムで使用している外部リソース(データベースハンドルなど)を常に整然と閉じる必要がある場合、ビットフェーザを使用すると、外部リソースが正しくタイムリーに解放されるようになります。

例えば、END フェーザー(Perl 5 では END ブロックと呼ばれます)を使用すると、(何らかの理由で)プログラムが終了するときにデータベースから適切に切断することができます。

DBIishconnect "Couldn't connect"
END disconnect


なお、Perl 6 では END フェーザーはブロック({ ... }など)を持つ必要はありません。そうでない場合、フェイザー内のコードは周囲のコードとレキシカルブロック(Lexpad)を共有します。

上記のコードには欠陥があります。プログラムが終了する前にデータベース接続が確立されていた場合、または何らかの理由でデータベース接続が失敗した場合、.disconnectメソッドにあるものをまだ呼び出そうとします。これは実行エラーになります。しかし、perl 6にはこれを避けるための簡単なイディオムがあります。と同じイディオムを使ってください。

END disconnect with  

.disconnectは$_.disconnectの略で、与えられた値(通常はインスタンス化されたオブジェクト)が$_に定義されテーマ化されている場合にのみ使用されます。

外部リソースをいつでもクリーンアップしたい場合は、スコープアウトすれば、LEAVE scoped phasesが使えるようになります。

DBIishconnect -> 
  LEAVE disconnect # no need for `with` here
  # do your stuff with the database
say "Could not do the stuff that needed to be done"

スコープ if は残され、あらゆる LEAVE フェイザーが実行されます。その結果、そのスコープでコードが実行されるたびに、データベースのリソースが解放されることになります。

概要

Perl 6 は、Perl 5 ユーザーが慣れているオブジェクトをタイムリーに破棄しませんが、Perl 5 の方法に似た、外部リソースの管理を確実に行うための使いやすい代替方法を備えています。

要約すると

この記事の内容が、あなたの勉強や仕事の参考になれば幸いです。また、スクリプトハウスを応援していただきありがとうございます。もっと詳しく知りたい方は、以下のリンク先をご覧ください。