PHP・GCの話-3話)変数データのメモリからの消滅



PHP・GCの話-3話)変数データのメモリからの消滅

前書き

  • すべての記事は、自分の勉強目的と主観の整理を含めています。あくまで参考レベルで活用してください。もし誤った情報などがあればご意見をいただけるととっても嬉しいです。
  • 内容では、省略するか曖昧な説明で、わかりづらいところもあると思います。そこは、連絡いただければ補足などを追加するので、ぜひ負担なくご連絡ください。
  • 本文での「GC」は、「Garbage Collection, Garbage Collector」の意味しており、略語として使われています。
  • この記事は、連載を前提に構成されています。

※ 連載目録

※ 連載で使うサンプルコード

Sample Code Link on Github

● ExampleGc.php : 2話から6話までの内容で使うサンプルコードです。
● ExampleWeakReference : 8話のWeakReferenceの内容で使うサンプルコードです。

本連載記事は、基本的にこのサンプルコードをベースに説明をしています。
サンプルコードは、必ず見る必要も実行してみる必要もありません。
各話ごとに、コードを分解して動作原理と結果を解説しますので、基本記事の内容で足りるように心がけます。
あくまで、全体コードをみたい、手元で回してみたい、修正して回してみたいという方向けです。

今回の話

今日は、PHPの変数の消滅に関して、以下のものを話そうと思うます。

    1. 変数のデータの消滅条件
    1. 変数のデータの消滅しない条件
    1. Summary

1. 変数のデータの消滅条件

私達が定義し使った変数の実際のデータは、消滅しないと、いつまでもメモリを専有します。
なので、開発言語は、適切に変数を消滅させるためのメカニズムが存在します。

PHPで、変数の実際のデータが消滅する条件は以下に定義できます。

① 「実際のデータの参照が一つ無効になる」時、zvalの参照カウント(zvalのrefcount)を-1した結果、0であればデータはメモリから消滅する。
② Garbage Collection Cycleの結果、参照カウントが0であれば、データはメモリから消滅する。

※ ②は、これから知っていくので、今は気にしなくて大丈夫です。

では、①の「実際のデータの参照が一つ無効になる」に関してですが、PHPでは、以下のメカニズムが働き、参照カウントが0であれば、データは消滅することになります。
参照カウント(refcount)が「0」であれば、データは消滅するという前提条件があることを意識して読んでいただくと良いと思います。

1) 変数の有効範囲の消滅

PHPの変数の有効範囲は、一部のケースを除いては「関数単位の単一範囲」になります。※1
その有効範囲から逃れる時、有効範囲自体の消滅と同時に、有効範囲内の変数シンボルは解除され、実際のデータの参照も無効になります。

1
2
3
4
5
function someFunc() {
$local = 'a'; //$localは、function{}の範囲内で有効です。
}

someFunc(); // 関数の遂行が合わったあと、a関数内の$localは自動的に解除されます。

2) 明示的な変数シンボルの解除(unset)

phpでは、変数シンボルの解除の機能の持つ関数としてunset()という関数を提供しています。
これは、あくまで該当変数シンボルを解除し、その変数の参照を無効にするだけで、変数データが消滅するか否かは、参照カウントによって決まることを意識すると良いです。

1
unset ( mixed $var [, mixed $... ] ) : void

3) 変数シンボルの参照再設定

変数シンボルの参照が再定義されると、以前の参照は無効になります。

1
2
$a = array(0);
$a = array(1); //この時、$aの変数の参照がarray(1)に上書きされ、array(0)に対する参照は無効になります。

2. 変数の消滅しない条件

逆に言いますと、変数が消滅しない条件というのは、以下と言えます。

「実際のデータの参照が一つ無効になる」時、参照カウント(refcount)が「1以上」であれば、データは消滅せずに残り続ける。

上記の条件になるケースを例えれば、以下のようなものがあります。

しかし、本内容は、該当のスコープからの解除時点でデータが消えないというだけであって、別のスコープで「変数の消滅条件」を満たし、明示的に消滅されることができるということを前提として参考にしてください。※2

1) 同じスコープで、同じデータを参照する変数がある場合

同じ関数内などの同じスコープで、同じデータを参照する変数が複数存在する場合、一つの変数が解除されても、データは消滅しません。

1
2
3
4
5
$a = array(0);
$b = $a;

unset($a);
var_dump($b); //$aの変数を解除しても、$bがarray(0)の参照を持っているため、消滅しません。

2) 違うスコープで、同じデータを参照する変数がある場合

関数の引数として渡された場合などは、上位スコープでまだデータを参照しているため、現在のスコープから変数が解除されても、データは消えません。

ここに関しては、引数渡しでのValue CopyとReference Copyの内容を参考にすると良いです。

1
2
3
4
5
6
7
8
function x(object $obj) {
unset($obj);
//現在のスコープの$objに対する参照は解除されます
//しかし、上位スコープにいる$aの変数の参照がまだ有効なため、データは消滅しません。
}

$a = array(0);
x($a);

3) compound typeの変数の内部で、同じデータを参照する変数がある場合

array, objectなどのcompound typeは、内部にデータは参照を持つことができます。
その内部でデータを参照していると、データは消滅しません。

1
2
3
4
5
6
7
8
function x(object $obj) {
$data = array(0);

$obj->data = $data;
//$dataは、x関数の遂行後に解除されますが、stdClassのインスタンスのメンバーとして、array(0)データを参照することになり、データは消滅しません。
}
$a = new \stdClass;
x($a);

3. Summary

今回で、最低限に覚えて頂くと良い内容は以下になります。

  • 参照カウント(refcount)が、0であればデータはメモリから消滅する。
  • 参照カウント(refcount)が、1以上であれば、データは消滅せずに残り続ける。

後書き

今回は、変数の消滅条件に関してお話をしました。

「既存の内容を参照カウント(refcount)の観点で再整理してみた」ということに目的がありますが、読んで頂く方の中では少しベタで当たり前だと感じるかもしれない内容に関して話した気もします。

その反面、他の方には逆に、少し不明なところがあるかもしれません。そういう時には、連絡頂けると補足いたしますので宜しくお願いします。

次回は、MemoryLeakと解除できない変数データに関して扱うことになります。

※注釈

※1
▶ PHPの変数の有効範囲は、一部のケースを除いては「関数単位の単一範囲」になります

一部のケースというのは、global scopeや、内部的に他のデータの参照を持つcompound type(object, array)の変数などなどを意味します。

※2
▶ 別のスコープで「変数の消滅条件」を満たし、明示的に消滅されることができるということを前提として参考にしてください。

ここでみたケースは、上位のスコープとかでプログラマーが明示的に消滅させることができますので、「永遠に消滅しない」という訳ではありません。しかし、GCなどの仕組みがなかったら、永遠に消滅しない変数データが、プログラマーの手で発生するケースもあります。その一つが、次回に扱う内容のMemory Leakと言えます。