PHP の GC の話 (勉強会発表資料)
昨日、第87回 PHP勉強会@東京 で PHP の GC について発表しました。発表資料を公開します。
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 は循環参照の候補から外す処理*2、GC_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 を比較しながらソースコードを読んでみるのも面白いと思います。