PHP null和copy-on-write

时间:2012-04-21 15:26:25

标签: php null micro-optimization reference-counting copy-on-write

假设我想要两个变量并使它们都等于null。 (更现实地说,我正在考虑一个包含大量null s的数组,但“两个变量”场景足以解决这个问题。)显然,我可以通过多种方式实现这一点。我可以这样做(方法1):

$a = null;
$b = $a;

根据我的理解,结果是符号表中的两个条目指向了一个zval:'a''b'。但另外一个人可能会这样做(方法2):

$a = null;
$b = null;

天真的人会期望这会导致两个不同的zval,每个zval都由符号表中的一个条目指向。

如果您希望拥有一个大型数组,并且该数组的许多元素将是null,那么创建一个{{1>会更有效(就zval /内存使用而言)值为$master_null的变量,然后通过使用null分配来编写数组的null元素?

3 个答案:

答案 0 :(得分:2)

考虑这个脚本:

$arr = array();
for ($i = 0; $i < 100000; $i++) $arr[] = null;
echo memory_get_usage() . "\n";

在我的机器上输出:21687696,即21 MB的已用内存。另一方面使用这个:

$master_null = null;
$arr = array();
for ($i = 0; $i < 100000; $i++) $arr[] = $master_null;
echo memory_get_usage() . "\n";

输出:13686832,即13 MB。根据这些信息,您可以假设,只要您关注内存使用情况,确实使用“master null”变量实际上更好。但是,您仍然需要拥有数组中的所有项,并且HashTable中的每个条目(数组的内部表示)也会占用一些内存。

如果您想深入研究zvals和参考文献,我建议使用函数debug_zval_dump。使用它,您可以看到哪些变量共享相同的zval:

$a = $b = $c = $d = "abc";
debug_zval_dump($a);
$x = $y = $z = $w = null; 
debug_zval_dump($x);
$q = null;
debug_zval_dump($q);

输出:

string(3) "abc" refcount(5)
NULL refcount(5)
NULL refcount(2)

这意味着虽然变量$ x和$ q都是NULL,但它们不是相同的zval。但是$ x和$ y共享相同的zval,因为它们是相互分配的。我相信您知道函数debug_zval_dump,但如果没有,请务必仔细阅读http://php.net/manual/en/function.debug-zval-dump.php处的引用说明。

同样在我的帖子结束时,我想说这些信息对于更好地了解PHP内部结构可能有用,我认为进行任何优化都没用。主要是因为有更好的地方开始优化脚本比这样的微优化。虽然这不是规范的一部分,但PHP作者可能会在将来更改此行为(例如,所有NULL变量在将来的某个版本中可能共享相同的zval。)

答案 1 :(得分:1)

据我所知,PHP zval容器有一个引用计数逻辑。因此,我的印象是,如果您使用引用,即&amp; $ master_null来初始化所有NULL值,我认为可以节省空间,即数组点的所有NULL项目都与zval容器的引用相同。

以下是一个例子:

# php -r '$var1 = NULL; $var2 = $var1; $var3 = $var1; debug_zval_dump(&$var1); debug_zval_dump(&$var2); debug_zval_dump(&$var3);'
&NULL refcount(2)
&NULL refcount(2)
&NULL refcount(2)

您可以在此处详细了解PHP的reference counting basis

值得从这个链接中读到的是:

PHP is smart enough not to copy the actual variable container
when it is not necessary. Variable containers get destroyed 
when the "refcount" reaches zero. The "refcount" gets decreased by 
one when any symbol linked to the variable container leaves the 
scope (e.g. when the function ends) or when unset() is called on a symbol.

因此,每次使用&amp; $ master_null时,都会增加“refcount”,当“refcount”达到零时,变量容器将从内存中删除。


从上面的评论示例中,这里是内存使用情况:

# php -r '$arr = array(); for ($i = 0; $i < 100000; $i++) $arr[] = null; echo memory_get_usage() . "\n";'
11248372
# php -r '$master_null = null; $arr = array(); for ($i = 0; $i < 100000; $i++) $arr[] = &$master_null; echo memory_get_usage() . "\n";'
6848488
# php -r '$master_null = null; $arr = array(); for ($i = 0; $i < 100000; $i++) $arr[] = $master_null; echo memory_get_usage() . "\n";'
6848468

答案 2 :(得分:0)

没有达到的目的是你会得到一个名为$master_null的额外变量。他们都指向null。让它们各自指向$master_null是一回事。