
PHP8 上級/準上級試験の認定模擬問題として、プライム・ストラテジー様のPRIME STUDYが認定されているようです。ただ、この模擬問題には解説がありませんので解説をまとめていこうと思います。シリーズ第21回目です。

PHP初級レベルから脱却して中級/上級レベルにいきたい人にピッタリの内容ですね。
PRIME STUDY認定模擬問題のリンクはこちらです → https://study.prime-strategy.co.jp/
問題文
PHP のメモリ消費 に関する説明の中で、誤っているものを1つ選びなさい。
また、すべてのコードには下記のコードが適切な箇所に書かれているものとする。
declare(strict_types=1);
error_reporting(-1);
(1)
PHP では、「プログラムが動的に確保したメモリ領域のうち、不要になった領域を自動的に解放する」いわゆるガベージコレクションが機能として存在する。
PHP のガベージコレクションは「参照カウント法」という方式で管理されている。
参照された数は、Xdebug がインストール済みであれば xdebug_debug_zval() 関数によって得る事ができる。
そのため、以下のコード
$obj = new stdClass();
xdebug_debug_zval('obj');
$obj2 = $obj;
xdebug_debug_zval('obj');
xdebug_debug_zval('obj2');
unset($obj);
xdebug_debug_zval('obj2');
を実行すると、結果は次のとおりとなる。
obj: (refcount=1, is_ref=0)=class stdClass { }
obj: (refcount=2, is_ref=0)=class stdClass { }
obj2: (refcount=2, is_ref=0)=class stdClass { }
obj2: (refcount=1, is_ref=0)=class stdClass { }
(2)
PHP の変数で、参照された数は、Xdebug がインストール済みであれば xdebug_debug_zval() 関数によって得る事ができる。
しかし int や string などの型の変数は、代入演算子 = によって「元の変数を新しい変数にコピーする (値による代入)」ために、通常の代入では refcount は増えない。
一方で参照による代入 & をすると refcount が増える。
そのため、以下のコード
$i = 1;
xdebug_debug_zval('i');
$i2 = $i;
xdebug_debug_zval('i');
$i3 = &$i;
xdebug_debug_zval('i');
を実行すると、結果は次のとおりとなる。
i: (refcount=0, is_ref=0)=1
i: (refcount=0, is_ref=0)=1
i: (refcount=2, is_ref=1)=1
(3)
PHP の変数で、参照された数は、Xdebug がインストール済みであれば xdebug_debug_zval() 関数によって得る事ができる。
オブジェクトを clone した場合は「オブジェクトのプロパティを 全てシャローコピーする」が、コピーオンライトによって内部的には参照が用いられているために、値を変更するまでの間は一時的に refcount が増える。
そのため、以下のコード
$obj = new stdClass();
xdebug_debug_zval('obj');
$obj2 = clone $obj;
xdebug_debug_zval('obj');
$obj2->tset = 'value';
xdebug_debug_zval('obj');
を実行すると、結果は次のとおりとなる。
obj: (refcount=1, is_ref=0)=class stdClass { }
obj: (refcount=2, is_ref=0)=class stdClass { }
obj: (refcount=1, is_ref=0)=class stdClass { }
(4)
PHP の変数はコピーオンライトが使われているため、参照ではないコピーであっても、コピーのタイミングではメモリはほとんど消費される事がない。
ただしコピー先の値に変更が加わると、そのタイミングで「実態がコピーされる」ために、一気にメモリが消費される。
そのため、以下のコード
$awk = range(0, 1000000);
var_dump(memory_get_usage(true));
$awk2 = $awk;
var_dump(memory_get_usage(true));
$awk2[] = 'v';
var_dump(memory_get_usage(true));
を実行すると、結果は次のとおりとなる。(値は環境によって変わる)
int(35655680)
int(35655680)
int(69214208)
解説
選択肢1の解説
正しいです。
xdebug_debug_zval() 関数はオブジェクトの参照カウント(refcount)を表示するため、参照カウントが増減する様子を確認することができます。$obj2 = $obj のように参照渡しを行った場合、参照カウントが増加することが確認できます。
選択肢2の解説
正しいです。
$i2 = $i の場合、値渡しなので参照カウントは増えませんが、$i3 = &$i の場合、参照渡しになるので参照カウントが増加します。この違いは xdebug_debug_zval() を使って確認できます。
選択肢3の解説
誤りです。クローン後にオブジェクトのプロパティが変更されることで、参照カウントが増えるのではなく、メモリがコピーされるため、参照カウントは変わらずメモリ使用量が増加します。
選択肢4の解説
正しいです。
PHP はコピーオンライト(copy-on-write)を使用しており、参照ではないコピーを作成しても、実際にはメモリの消費は増えません。コピー先の値に変更が加わるまで、オリジナルとコピーは同じデータを指しています。そのため、変更が加わった時点でメモリが一気に消費されます。
正解選択肢
以上より選択肢3が正解となります。
コメント