y_uti のブログ

統計、機械学習、自然言語処理などに興味を持つエンジニアの技術ブログです

PHP の GC の話 (勉強会発表資料)

昨日、第87回 PHP勉強会@東京PHPGC について発表しました。発表資料を公開します。

PHP の処理系に実装されている、参照カウント方式の GC と循環参照によるごみの回収について、比較的平易に説明したつもりです。

GC について (お金を使わずに) 勉強してみたい方は、英語の情報ですが以下のページが参考になります。このページに Surveys として紹介されている 3 本の論文で基本的な内容は理解できると思います*1
Richard Jones' Garbage Collection Page

最初の "Uniprocessor Garbage Collection Techniques" は GC のさまざまなアルゴリズムについて説明したもの、次の "Survey of Distributed Garbage Collection Techniques" は分散 GC についての説明です。最後の "Dynamic Storage Allocation: A Survey and Critical Review" は、メモリ割り当ての戦略について説明しています。GC とは少し別の話題になりますが、プログラム実行時の動的なメモリ割り当てではフラグメンテーションの問題なども考慮する必要があり、要求されたサイズの領域を空き領域全体のどこから切り出して割り当てるかという点にも、色々と工夫の余地があります。上述のウェブサイトのリンク先は PostScript 形式のファイルだったりするのですが、それぞれ Google で論文名で検索すると PDF のファイルも見つかります。

さて、GC は、こういった文献で説明されるアルゴリズムと実際の処理系のソースコードを照らし合わせながら読むのが楽しいと思うのですが、勉強会の発表では具体的な実装についての話はできませんでした。PHPソースコード上では、循環参照を回収する処理は Zend/zend_gc.c に実装されています。gc_collect_cycles 関数から始まり、アルゴリズムの各フェーズがそれぞれ関数になっていて、かなり分かりやすく書かれている印象を受けました。
php-src/zend_gc.c at php-5.6.5 · php/php-src · GitHub

参照カウントの増減は、Zend/zend.h の zval_addref_p, zval_delref_p にあります。処理系の他の部分からは Z_ADDREF_P, Z_DELREF_P といったマクロを経由して呼ばれるようです。例として Zend/zend_execute.c の zend_assign_to_variable 関数を見てみると、参照カウントが 1 (減らすと 0) の場合 はその領域を解放し、それ以外 (2 以上) の場合は Z_DELREF_P で参照カウントを減らしています。GC_REMOVE_ZVAL_FROM_BUFFER は循環参照の候補から外す処理*2GC_ZVAL_CHECK_POSSIBLE_ROOT は循環参照の候補に加える処理です。こうして読んでみると、アルゴリズムとの対応が何となく分かるでしょうか。
php-src/zend_execute.c at php-5.6.5 · php/php-src · GitHub

現在 PHP は 7.0 の開発が進められていますが、これらの基本的なファイル構成や GCアルゴリズムは PHP7 でも変わらないようです。その一方で、内部の個々のデータ構造などは大きくリファクタリングされ、メモリ使用量の削減、実行速度の改善が得られています。PHP5 と PHP7 を比較しながらソースコードを読んでみるのも面白いと思います。

*1:いずれも古い論文なので、最新の話題は載っていないと思いますが。そういう私も最近の研究はまったく追えていません。

*2:領域を解放するので、それが循環参照の候補だった場合は外してあげる必要があります。