♻️
【PHPメモリ管理】関数内の変数はいつ解放されるの?
はじめに
最近、私は会社の先輩からPHPのメモリ管理について教えていただきました。
特に、関数内の変数が解放されるタイミングについての知識は、パフォーマンスの向上に直結する重要な要素です。ですので今回は、解放されるタイミングについて、基本的な概念を皆さんに紹介したいと思います。
参考
PHP変数の保管場所「zval」
まず、PHP変数はどこに保管されるのでしょうか?
PHP変数は zval
コンテナに保管されます。
zval
コンテナの構成
- 変数の型
- 変数の値
- 情報の追加ビット
- is_ref
- refcount
is_ref:変数が「参照集合」の一部かどうかを示すbool値
- このビットによって、通常の変数と参照を区分する方法をPHPエンジンが知ります。
- PHPではユーザーランドで参照を使えるので、
zval
もメモリー使用状況を最適化するための内部的なリファレンスカウント構造を持っています。
refcount:1つのzvalをどれだけ多くの変数名(シンボル)が指すか
- 変数名(シンボル)はすべてシンボルテーブルに保管されます。
- スコープごとに1つのシンボルテーブルがあります。
- 関数やメソッドごとのスコープばかりではなく、メインスクリプト用スコープ(ブラウザによってリクエストされたスクリプト)もあります。
例1)zval 情報を確認する
<?php
$a = "new string";
xdebug_debug_zval('a');
新しい変数名(a
)が現在のスコープで作成され、新しい変数コンテナがstring型と値(new string)で作成されます。
a: (refcount=1, is_ref=0)='new string'
-
is_ref : ユーザーランド参照が作成されたことがないので、
is_ref
ビットはデフォルトで false にセットされます。 -
refcount : この変数コンテナを利用する変数が1つだけあるので、
refcount
は1に設定されます。
※ refcount
を持つ参照(is_ref
ビットが true の場合)が1の場合、参照されていないかのように(= is_ref
が常に false であったかのように)扱われる点に注意してください。
例2)zval の refcount を増加
<?php
$a = new stdClass();
$b = $a;
xdebug_debug_zval( 'a' );
a: (refcount=2, is_ref=0)=class stdClass { }
同じ変数コンテナがa
とb
にリンクされるので、refcount
は2になります。
xdebug_debug_zval()
で関数内の変数が解放されるタイミングを確認する
上記で記述したzval
を利用して関数内の変数のメモリはいつ解放されるか確認してみましょう。
テストのために値を受け取りつつ、何もせずに終了する関数を使用します。
<?php
/**
* @param stdClass $p パラメータ
* @return void
*/
function nothing(stdClass $p): void
{
xdebug_debug_zval('p');
return;
}
$a = new stdClass();
xdebug_debug_zval('a');
nothing($a);
xdebug_debug_zval('a');
a: (refcount=1, is_ref=0)=class stdClass { }
p: (refcount=3, is_ref=0)=class stdClass { }
a: (refcount=1, is_ref=0)=class stdClass { }
実行結果を見てみましょう。
nothing($a);
で関数内の$p
が$a
にリンクされている変数コンテナにリンクされるので、refcount が増加します。
p: (refcount=3, is_ref=0)=class stdClass { }
その後、関数が終わる(スコープを抜ける)時点で、PHPは自動的に関数内の変数を解放しますので refcount が減少します。
a: (refcount=1, is_ref=0)=class stdClass { }
この結果から、関数内(ローカルスコープ)の変数は関数終了時に解放されることが確認できました。ですので、私たちは関数内の変数のメモリについてはあまり意識しなくても良いかと思います。
Discussion