現在の言語: 日本語

戻る

zvalコンテナ
メモリ解放関連

zvalコンテナはphpの変数やデータを管理しています。

phpのデータを格納します。
データ型に応じてメンバーが使用されます

zvalコンテナの型です。
phpの型<内部型定数/td>説明
boolIS_TRUE
IS_FALSE
PHP 7.2以降では、真偽値それぞれが個別の内部型定数を持つようになりました。
それ以前は IS_BOOL と zend_bool でした。
intIS_LONG整数型は内部的には常にC言語の long 型として格納されます。
stringIS_STRING文字列データです。変更されるまでコピーが発生しない効率的な管理が行われます。
floatIS_DOUBLE浮動小数点数は内部的にはC言語の double 型として格納されます。
objectIS_OBJECTクラスのインスタンスです。zval内にオブジェクト構造体へのポインタを保持します。
resourceIS_RESOURCEファイルポインタやデータベース接続など、外部リソースへの参照を管理するための型です。
nullIS_NULL値が存在しないことを示します。
値フィールドは使用されません。
arrayIS_ARRAY配列はzval内にハッシュテーブル(HashTable)構造体へのポインタを保持します。

リファレンスカウント
zvalコンテナが変数名が参照している内容を追跡することができるようにした
追跡カンターです。
is_refフラグ
phpでは参照渡しが使えます。
その参照渡しを使った場合、フラグが立ちます。


[サンプル]

copy
class test1
{
	/*
	PHPは引数に & があるため、$a が指している元のzvalコンテナをそのまま $num に共有させます
	refcountは1から2に増えます。
	参照状態になったためis_refは1に変更します(is_refはインクリメントされることはありません)
	*/
	function test2(int &$num) 
	{
		$num++;      //4
		/*
		is_refフラグが1になったためCOW(Copy on write)は行われません
		$aと$numは同じデータを見ているのではありません
		同じメモリ領域を共有して、どちらかが変更した場合は反対も反映されるという関係となります。
		そのため、解放処理は実行されず
		値のみが1から2に変更します
		refcount:2
		is_ref:1
		*/
		$b=$num;     //5
		/*
		refcount:3
		is_ref:1
		*/
		$c=$num+$b;  //6
		/*
		値2:
		refcount:3(参照が$aと$numと$b)
		is_ref:1
		値4:(計算結果)
		refcount:1
		is_ref:0
		*/
		unset($c);   //7
		/*
		値2はそのまま
		値4の参照が切断され、即時解放されます
		値4:(計算結果)
		refcount:0(1から0に変更)
		is_ref:0

		*/
		$b=null;     //8
		/*
		nullを設定することで値2(4b)の参照が1個切断され2個から1個に変更します。
		refcount:2(参照が$aと$num)
		is_ref:1
		*/
	}
	function test1()
	{
		$a=1;            //1
		/*
		値1を持つzvalコンテナが作成されます。
		$aが値1を指します。
		refcount:1
		is_ref:0
		*/
		$this->test2($a);//2
		/*
		test2が終了すると$numがスコープから消え
		refcountが2かr1に変更します
		しかし$aという変数がzvalを参照しているため
		is_refフラグはまだ1のままとなります。
		refcount:1($aの分)
		is_ref:1(zvalを参照しているため)
		*/
		unset($a);       //9
		/*
		refcount:0($aが即時解放)
		is_ref:0
		*/
	}
}
echo "<pre>";
$cls1= new test1();
$cls1->test1();
echo "</pre>";
copy
class test1
{
	/*
	Because PHP uses the & argument, the original zval container pointed to by $a is shared with $num.
	The refcount increases from 1 to 2.
	Because it is now a reference, is_ref changes to 1 (is_ref is never incremented).
	*/
	function test2(int &$num)
	{
		$num++; //4
		/*
		Since the is_ref flag is now 1, no COW (Copy on Write) is performed.
		$a and $num do not actually view the same data.
		They share the same memory area, and changes to either are reflected in the other.
		Therefore, no release process is performed.
		Only the value changes from 1 to 2.
		refcount:2
		is_ref:1
		*/
		$b=$num; //5
		/*
		refcount:3
		is_ref:1
		*/
		$c=$num+$b; //6
		/*
		Value 2:
		refcount:3 (references are $a, $num, and $b)
		is_ref:1
		Value 4: (calculation result)
		refcount:1
		is_ref:0
		*/
		unset($c); //7
		/*
		Value 2 remains the same.
		The reference to value 4 is severed and immediately released.
		Value 4: (calculation result)
		refcount:0 (changes from 1 to 0)
		is_ref:0
		
		*/
		$b=null; //8
		/*
		By setting null, one reference to value 2 (4b) is severed, changing it from two to one.
		refcount: 2 (references are $a and $num)
		is_ref: 1
		*/
	}
	function test1()
	{
		$a=1; //1
		/*
		A zval container with value 1 is created.
		$a points to value 1.
		refcount: 1
		is_ref: 0
		*/
		$this->test2($a);//2
		/*
		When test2 ends, $num goes out of scope,
		and the refcount changes from 2 to 1.
		However, because the $a variable references a zval,
		the is_ref flag remains 1.
		refcount: 1 (for $a)
		is_ref: 1 (because it references a zval)
		*/
		unset($a); //9
		/*
		refcount: 0 ($a is immediately released)
		is_ref: 0
		*/
	}
}
echo "<pre>";
$cls1 = new test1();
$cls1->test1();
echo "</pre>";

参照渡しの関数を実行したケースです。
copy
class test2
{
	/*
	$aと$numの2個の参照カウンタ、$numは参照渡しを受け取っているので参照フラグが1となります。
	refcount:2
	is_ref:1
	*/
	function test1(int &num)//3
	{
		test2($num);//4
		/*
		test2($num); (実行前)
		refcount:2
		is_ref:1

		このtest1関数のスコープを抜けると
		$numが解放されるため
		refcount:1
		is_ref:1
		となります
		*/
	}
	/*
	$aと$num(test1)と$num(test2)の3個の参照カウンタ
	参照セットの状態は継続するためis_refフラグは1のままとなります。
	refcount:3
	is_ref:1
	*/
	function test2(int &num) //5
	{
		$num++;//6
		/*
		is_refが1なので、PHPは新しいzvalを作らず、既存のzvalの値10を11に書き換えます。
		refcount:3
		is_ref:1
		このtest2関数のスコープを抜けると
		$numが解放されるため
		refcount:2
		is_ref:1
		となります
		*/
	}
}
echo "<pre>";

$cls2 = new test2();
$a=10;//1
/*
値 10 のzvalコンテナがd作成
されます
refcount:1
is_ref:0
*/
$cls2->test1($a);//2
/*
test1($a); (実行前)
refcount:1
is_ref:0

test1関数のスコープを抜けると
refcount:1
is_ref:1
となります。
is_refが1なのは
$aという変数がzvalを参照しているためです。
*/
unset($a);//7
/*
unset関数を実行することで$aは即時解放します
しかし、is_refは1のままとなります。
refcount:0
is_ref:1
これはzvalコンテナが意図的に参照(&)した結果、複数の変数シンボルによって
共有されている状態になった場合です。
一度、is_refが1に設定されると参照セットとして扱います。
このis_refが参照セットを解消するのは(1)かつ(2)の条件を満たす場合となります。
(1)
参照している変数だけが1個だけとなり、その変数が通常の変数に戻ったケース
(2)
参照している変数がすべてスコープから消えるもしくはunset()されたケース
(例)
$a = $a + 0;//値は変えたくないため0を加算して普通の変数とする
もしくは
$a =(int)$a;//キャストをすることで普通の変数とする
このようにすると
is_refが1から0に変更します
また、unset($a)により参照元がゼロになり
zvalコンテナごと解放された場合はis_refのライフサイクルも終了します。

*/

echo "</pre>";
copy
class test2
{
	/*
	There are two reference counters, $a and $num. Since $num is passed by reference, the reference flag is set to 1.
	refcount:2
	is_ref:1
	*/
	function test1(int &num)//3
	{
		test2($num);//4
		/*
		test2($num); (Before execution)
		refcount:2
		is_ref:1
		
		When the scope of this test1 function leaves,
		$num is released, so
		refcount:1
		is_ref:1
		
		*/
	}
	/*
	There are three reference counters, $a, $num(test1), and $num(test2).
	The reference set state continues, so the is_ref flag remains 1.
	refcount:3
	is_ref:1
	*/
	function test2(int &num) //5
	{
		$num++;//6
		/*
		Since is_ref is 1, PHP does not create a new zval, but instead rewrites the existing zval value 10 to 11.
		refcount:3
		is_ref:1
		When the scope of this test2 function is exited,
		$num is released,
		so refcount:2
		is_ref:1
		becomes.
		*/
	}
}
echo "<pre>";

$cls2 = new test2();
$a=10;//1
/*
A zval container with a value of 10 is created.
refcount:1
is_ref:0
*/
$cls2->test1($a);//2
/*
test1($a); (Before execution)
refcount:1
is_ref:0

When the scope of the test1 function is exited,
refcount:1
is_ref:1
becomes.
is_ref is 1 because
the variable $a references a zval.
*/
unset($a);//7
/*
Executing the unset function immediately releases $a.
However, is_ref remains 1.
refcount:0
is_ref:1
This occurs when a zval container is intentionally referenced (&) and is shared by multiple variable symbols.
Once is_ref is set to 1, it is treated as a reference set.
This is_ref will dissolve the reference set when both conditions (1) and (2) are met.
(1)
When only one referenced variable remains and that variable reverts to a normal variable.
(2)
When all referenced variables disappear from scope or are unset()ed.
(Example)
$a = $a + 0; // Do not change the value, so add 0 to make it a normal variable.
or
$a = (int)$a; // Cast to make it a normal variable.
Doing this will change
is_ref from 1 to 0.
Also, unset($a) will reset the reference source to zero,
and when the entire zval container is released, the is_ref lifecycle will also end.

*/

echo "</pre>";
参照渡しの関数を実行したケースです。
内部で更に参照渡しの関数を実行した例です。


戻る

著作権情報
ホームページおよプリ等に掲載されている情報等については、いかなる保障もいたしません。
ホームページおよびアプリ等を通じて入手したいかなる情報も複製、販売、出版または使用させたり、
または公開したりすることはできません。
当方は、ホームページおよびアプリ等を利用したいかなる理由によっての障害等が発生しても、
その結果ホームページおよびアプリ等を利用された本人または他の第三者が被った損害について
一切の責任を負わないものとします。