現在の言語: 日本語

戻る

参照カウント
メモリ解放関連

参照カウントについて
参照カウンタの目的はメモリ解放処理にあります。
カウンタがゼロになった場合はメモリが使われていない状態を示すので
メモリの(即時)解放を実行します。
phpではzvalと呼ばれるコンテナでメモリを管理しています。
参照カウンタは、特定の値をいくつの変数や配列要素などが
参照しているのか?を管理して
追跡できるようにした仕組みです。

参照カウンタがインクリメントするケース
値への参照が作成されるごと
参照カウンタがデクリメントするケース
参照が削除されるごと
(例)
変数のスコープが終了
unset()を実行
nullを設定

処理参照カウンタの結果状態
$data =nulllカウンタが1減ります。変数は存在しますが、null値を保持しています。
unset($data);カウンタが1減ります。変数自体が未定義になります。

循環参照があるケース
unset関数を実行しても参照カウンタはゼロにはならないため
メモリは即時解放とはなりません。
そして、ガベージコレクションが実行されるのを待つことになります。
待たずに循環参照を解決するには、循環参照をしているいずれかに対して
nullで参照を切る必要があります。

[サンプル]

copy
class test1
{
	private function test2(int $num)//$numに$aが値渡しされ参照カウンタが1になります。
	{
		$b=0;//$bの参照カウンタが2になります($aと$numを参照)
		$b=$num;
		/*
		0($b)の参照カウンタが1から0になり、参照が切断され解放されます
		$bに$numの値である100がコピーされます
		値0は参照カウンタが0になり、即時解放し
		値100が$bと$numと$aの3個の参照カウンタとなります
		*/
		$b++;
		/*
		100から101にインクリメントします
		このため100が$bから参照されなくなり、$numのみからしか参照されません
		そして101にインクリメントした値を$bが参照します
		値100の参照が($aと$numと$b)でしたが、$bの参照が切れ($aと$num)になり、2個の参照となります
		値101(インクリメントによる結果)には$bが参照するので1個の参照カウンタとなります
		*/
		$c=$num+$b;//5
		/*
		$num(100)+$b(101)が計算され、結果の値201が$cに参照されます
		値201に$cの1個の参照カウンタが発生します
		*/
		echo $c.PHP_EOL;//6
		/*
		出力処理が実行されますが、参照カウンタには変動がありません
		*/
		unset($c);//7
		/*
		$cの参照が切断され、値201を参照するものがなくなります。
		値201は参照が切断され参照カウンタが0となり即時解放されます
		*/
		$num=null;
		/*
		値100を参照していた$aとnumのうち$numがnullにより参照の切断をされます
		このため値100を参照している変数が$aの1個となります
		*/
		unset($num);
		/*
		$numが参照しているものはなにもありません。
		変数が未定義となります
		*/
	}
	function test1()
	{
		$a=100;
		/*
		値100がメモリに生成され、$aがそれを参照します。
		参照カウンタが1となります
		*/
		$this->test2($a);
		echo $a.PHP_EOL;
		/*
		test2()が呼び出されます。
		$aの値100がtest2()の$numに「値渡し」されますが、
    CoWにより、値100のRCは一時的に2になります。
		*/
		$a=null;
		/*
		aの参照が切れ、値100の参照が1から0になり、値100は即時解放されます。
		*/
		unset($a);
		/*
		$aにnullであり値への参照はありません
		unsetを実行する3ことで$a変数が未定義になります
		*/
	}
}
echo "<pre>";
$cls1= new test1();
$cls1->test1();
echo "</pre>";
copy
class test1
{
	private function test2(int $num) //$a is passed by value to $num, and the reference counter is set to 1. {
		$b=0; // The reference counter of $b becomes 2 (references $a and $num)
		$b=$num;
		/*
		The reference counter of 0 ($b) changes from 1 to 0, and the reference is severed and released.
		The value of $num, 100, is copied to $b.
		The reference counter of the value 0 becomes 0 and is immediately released.
		The value 100 becomes three reference counters: $b, $num, and $a.
		*/
		$b++;
		/*
		Increment from 100 to 101.
		As a result, 100 is no longer referenced by $b, and is only referenced by $num.
		And $b references the incremented value to 101.
		The references to the value 100 were ($a, $num, and $b), but $b's reference is severed ($a and $num), so there are now two references.
		The value 101 (the result of the increment) is referenced by $b, so there is now only one reference counter.
		*/
		$c=$num+$b;//5
		/*
		$num(100)+$b(101) is calculated, and the resulting value, 201, is referenced by $c.
		The value 201 has a reference counter of 1 for $c.
		*/
		echo $c.PHP_EOL;//6
		/*
		The output process is executed, but the reference counter remains unchanged.
		*/
		unset($c);//7
		/*
		The reference to $c is severed, and nothing references the value 201.
		The reference to the value 201 is severed, its reference counter drops to 0, and it is immediately released.
		*/
		$num=null;
		/*
		Of the variables $a and num that referenced the value 100, $num is nulled, causing it to lose its reference.
		As a result, the only variable that references the value 100 is $a.
		*/
		unset($num);
		/*
		$num no longer references anything.
		The variable becomes undefined.
		*/
		}
		function test1()
		{
		$a=100;
		/*
		The value 100 is created in memory and referenced by $a.
		The reference counter becomes 1.
		*/
		$this->test2($a);
		echo $a.PHP_EOL;
		/*
		test2() is called.
		The value 100 of $a is "passed by value" to $num of test2().
		Due to CoW, the RC of 100 temporarily becomes 2.
		*/
		$a=null;
		/*
		The reference to a is lost, the reference to 100 changes from 1 to 0, and 100 is immediately released.
		*/
		unset($a);
		/*
		$a is null and there is no reference to its value.
		Executing unset() makes the $a variable undefined.
		*/
	}
}
echo "<pre>";
$cls1=new test1();
$cls1->test1();
echo "</pre>";

通常のケースです。
copy
class test2
{
	private function test2(int &$num)//$numは$aのエイリアスとして機能します。同じメモリ位置を指すため参照カウンタは増えません
	{
		$b=0;
		/*
		値0がメモリに生成され、$bがそれを参照します。
		*/
		$b=$num;
		/*
		$bの値が変更しています。
		このため
		値0の$bからの参照が切れ、値0の参照カウンタは1から0となり、即時解放します
		$bが$num($aのエイリアス)と同じ値100を参照し始めます。
		値100は$numと$bの2個の参照カウンタとなります
		*/
		$b++;
		/*
		$bの値が変更されるため、書き込み時コピー(CoW)が働きます。
		値100が複製され、新しい値101が生成されます。
		$bは新しい値101を参照します。
		古い値100のRCは2から1 に減少します。($a/$numのみが参照)
		新しい値101のRCは1です。($bが参照)
		*/
		$c=$num+$b;
		/*
		$num(100)+$b(101)の計算結果(201)を$cが参照し、参照カウンタが1となります
		値100($numが参照)の参照カウンタは1のまま
		値101($bが参照)の参照カウンタも1のまま
		となります
		*/
		echo $c.PHP_EOL;
		/*
		出力処理が実行されますが、参照カウンタは変更しません
		*/
		unset($c);
		/*
		$cの参照が切れるため値201の参照がなくなります。
		そのため値201は即時解放となります
		また、$cは未定義となります
		*/
		$num=null;
		/*
		$numは$aのエイリアスなので、$aにもnullが代入されます。
		$aと$numの両方が値100への参照を同時に切ります。
		値100のRCは 1 から 0 になり、即時解放されます。
		この時点で、$aも$numも「nullという値を持つ変数」になります。
		*/
		unset($num);
		/*
    $numは$aのエイリアスなので、$numをunsetすると、$aの変数定義そのものも同時に削除されます。
    $num(と$a)は完全に未定義状態となります。
    関数を抜ける際、$b(値101を参照中)のスコープが終了するため、値101のRCが0になり、即時解放されます。
		*/
	}
	function test1()
	{
		$a=100;
		/*
		値100を$aが参照し、参照カウンタが1になります
		*/
		$this->test2($a);
		/*
		test2の実行後、$aはtest2内でunsetされたため、完全に未定義状態になっています。
		*/
		echo $a;
		/*
		$aは未定義 (undefined variable) のため、Warning/Errorが発生し、期待通りの出力はされません。
		*/
		$a=null;
		/*
		$aはすでに未定義なので、この行は実行されません(到達不能コード)。
		*/
		unset($a);
		/*
		これも実行されません(到達不能コード)。
		*/
	}
}
echo "<pre>";

$cls2 = new test2();
$cls2->test1();

echo "</pre>";
copy
class test2
{
	private function test2(int &$num) //$num acts as an alias for $a. Because it points to the same memory location, the reference counter is not incremented.
	{
		$b=0;
		/*
		A value of 0 is created in memory, and $b references it.
		*/
		$b=$num;
		/*
		The value of $b changes.
		As a result,
		$b's reference to value 0 is broken, and its reference counter drops from 1 to 0, causing it to be immediately released.
		$b now references the same value 100 as $num (an alias for $a).
		The value 100 has two reference counters: $num and $b.
		*/
		$b++;
		/*
		Because the value of $b changes, copy-on-write (CoW) is triggered.
		The value 100 is duplicated, creating a new value 101.
		$b references the new value 101.
		The RC of the old value 100 is decremented from 2 to 1. (Only $a/$num references)
		The RC of the new value 101 is 1. (Referenced by $b)
		*/
		$c=$num+$b;
		/*
		$c references the result (201) of $num(100) + $b(101), and its reference counter becomes 1.
		The reference counter of value 100 (referenced by $num) remains 1.
		The reference counter of value 101 (referenced by $b) also remains 1.
		
		*/
		echo $c.PHP_EOL;
		/*
		The output process is executed, but the reference counter does not change.
		*/
		unset($c);
		/*
		The reference to $c is lost, so there is no reference to value 201.
		Therefore, value 201 is immediately released.
		Also, $c becomes undefined.
		*/
		$num=null;
		/*
		Since $num is an alias for $a, null is assigned to $a as well.
		Both $a and $num simultaneously cut their references to the value 100.
		The RC of the value 100 changes from 1 to 0, and it is immediately released.
		At this point, both $a and $num become "variables with the value null."
		*/
		unset($num);
		/*
		$num is an alias for $a, so unsetting $num also deletes the variable definition of $a.
		$num (and $a) become completely undefined.
		When exiting the function, the scope of $b (which is referencing the value 101) ends, so the RC of the value 101 becomes 0 and it is immediately released.
		*/
		}
		function test1()
		{
		$a=100;
		/*
		$a references the value 100, and its reference counter becomes 1.
		*/
		$this->test2($a);
		/*
		After test2 is executed, $a is completely undefined because it was unset within test2.
		*/
		echo $a;
		/*
		$a is undefined (an undefined variable), so a warning/error occurs and the expected output is not produced.
		*/
		$a=null;
		/*
		$a is already undefined, so this line is not executed (unreachable code).
		*/
		unset($a);
		/*
		This line is also not executed (unreachable code).
		*/
	}
}
echo "<pre>";

$cls2 = new test2();
$cls2->test1();

echo "</pre>";
参照渡しを使用したケースです。


戻る

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