我意识到第二种方法避免了函数调用的开销(更新,实际上是一种语言构造),但知道其中一种方法是否比另一种更好会很有趣。我的大部分代码都使用unset(),但最近我在网上找到了一些使用$var = null的体面类。

是否有一个首选的,理由是什么?


当前回答

我为unset和=null创建了一个新的性能测试,因为正如在评论中提到的,这里写的有一个错误(重新创建元素)。 我用的是数组,你们看,现在无所谓了。

<?php
$arr1 = array();
$arr2 = array();
for ($i = 0; $i < 10000000; $i++) {
    $arr1[$i] = 'a';
    $arr2[$i] = 'a';
}

$start = microtime(true);
for ($i = 0; $i < 10000000; $i++) {
    $arr1[$i] = null;
}
$elapsed = microtime(true) - $start;

echo 'took '. $elapsed .'seconds<br>';

$start = microtime(true);
for ($i = 0; $i < 10000000; $i++) {
    unset($arr2[$i]);
}
$elapsed = microtime(true) - $start;

echo 'took '. $elapsed .'seconds<br>';

但是我只能在PHP 5.5.9服务器上测试它,结果如下: -耗时4.4571571350098秒 -耗时4.4425978660583秒

出于可读性考虑,我更喜欢不设置。

其他回答

来自注释的代码示例

echo "PHP Version: " . phpversion() . PHP_EOL . PHP_EOL;

$start = microtime(true);
for ($i = 0; $i < 10000000; $i++) {
    $a = 'a';
    $a = NULL;
}
$elapsed = microtime(true) - $start;

echo "took $elapsed seconds" . PHP_EOL;



$start = microtime(true);
for ($i = 0; $i < 10000000; $i++) {
    $a = 'a';
    unset($a);
}
$elapsed = microtime(true) - $start;

echo "took $elapsed seconds" . PHP_EOL;

在docker容器中运行image php:7.4-fpm等。

PHP Version: 7.4.8
took 0.22569918632507 seconds null
took 0.11705803871155 seconds unset
took 0.20791196823121 seconds null
took 0.11697316169739 seconds unset

PHP Version: 7.3.20
took 0.22086310386658 seconds null
took 0.11882591247559 seconds unset
took 0.21383500099182 seconds null
took 0.11916995048523 seconds unset

PHP Version: 7.2.32
took 0.24728178977966 seconds null
took 0.12719893455505 seconds unset
took 0.23839902877808 seconds null
took 0.12744522094727 seconds unset

PHP Version: 7.1.33
took 0.51380109786987 seconds null
took 0.50135898590088 seconds unset
took 0.50358104705811 seconds null
took 0.50115609169006 seconds unset

PHP Version: 7.0.33
took 0.50918698310852 seconds null
took 0.50490307807922 seconds unset
took 0.50227618217468 seconds null
took 0.50514912605286 seconds unset

PHP Version: 5.6.40
took 1.0063569545746 seconds null
took 1.6303179264069 seconds unset
took 1.0689589977264 seconds null
took 1.6382601261139 seconds unset

PHP Version: 5.4.45
took 1.0791940689087 seconds null
took 1.6308979988098 seconds unset
took 1.0029168128967 seconds null
took 1.6320278644562 seconds unset

但是,再举一个例子:

<?php
ini_set("memory_limit", "512M");

echo "PHP Version: " . phpversion() . PHP_EOL . PHP_EOL;

$start = microtime(true);
$arr = [];
for ($i = 0; $i < 1000000; $i++) {
    $arr[] = 'a';
}
$arr = null;
$elapsed = microtime(true) - $start;

echo "took $elapsed seconds" . PHP_EOL;



$start = microtime(true);
$arr = [];
for ($i = 0; $i < 1000000; $i++) {
    $arr[] = 'a';
}
unset($arr);
$elapsed = microtime(true) - $start;

echo "took $elapsed seconds" . PHP_EOL;

结果:

PHP Version: 7.4.8
took 0.053696155548096 seconds
took 0.053897857666016 seconds

PHP Version: 7.3.20
took 0.054572820663452 seconds
took 0.054342031478882 seconds

PHP Version: 7.2.32
took 0.05678391456604 seconds
took 0.057311058044434 seconds


PHP Version: 7.1.33
took 0.097366094589233 seconds
took 0.073100090026855 seconds

PHP Version: 7.0.33
took 0.076443910598755 seconds
took 0.077098846435547 seconds

PHP Version: 7.0.33
took 0.075634002685547 seconds
took 0.075317859649658 seconds

PHP Version: 5.6.40
took 0.29681086540222 seconds
took 0.28199100494385 seconds

PHP Version: 5.4.45
took 0.30513095855713 seconds
took 0.29265689849854 seconds

在2009年的unset手册页面中提到:

unset() does just what its name says - unset a variable. It does not force immediate memory freeing. PHP's garbage collector will do it when it see fits - by intention as soon, as those CPU cycles aren't needed anyway, or as late as before the script would run out of memory, whatever occurs first. If you are doing $whatever = null; then you are rewriting variable's data. You might get memory freed / shrunk faster, but it may steal CPU cycles from the code that truly needs them sooner, resulting in a longer overall execution time.

(自2013年起,未设置的手册页不再包括该部分)

注意,在php5.3之前,如果循环引用中有两个对象,例如在父子关系中,在父对象上调用unset()将不会释放用于子对象中父引用的内存。当父对象被垃圾回收时,内存也不会被释放。


问题“unset和= null之间的差异”详细说明了一些差异:


Unset ($a)也从符号表中删除$a;例如:

$a = str_repeat('hello world ', 100);
unset($a);
var_dump($a);

输出:

Notice: Undefined variable: a in xxx
NULL

但是当使用$a = null时:

$a = str_repeat('hello world ', 100);
$a = null;
var_dump($a);

输出:

NULL

$a = null似乎比它的unset()对手快一点:更新符号表项似乎比删除它快。


当您尝试使用不存在(未设置)变量时,将触发一个错误,变量表达式的值将为null。(因为,PHP还应该做什么?每个表达式都需要产生某个值。) 一个赋值为null的变量仍然是一个完全正常的变量。

<?php
$start = microtime(true);
for ($i = 0; $i < 10000000; $i++) {
    $a = 'a';
    $a = NULL;
}
$elapsed = microtime(true) - $start;

echo "took $elapsed seconds\r\n";



$start = microtime(true);
for ($i = 0; $i < 10000000; $i++) {
    $a = 'a';
    unset($a);
}
$elapsed = microtime(true) - $start;

echo "took $elapsed seconds\r\n";
?>

根据这一点,“= null”似乎更快。

PHP 5.4结果:

花了0.88389301300049秒 耗时2.1757180690765秒

PHP 5.3结果:

耗时1.7235369682312秒 用时2.9490959644318秒

PHP 5.2结果:

花了3.0069220066071秒 耗时4.7002630233765秒

PHP 5.1结果:

花了2.6272349357605秒 耗时5.0403649806976秒

在PHP 5.0和4.4中,情况开始有所不同。

5.0:

花了10.038941144943秒 耗时7.0874409675598秒

4.4:

耗时7.5352551937103秒 花了6.6245851516724秒

请记住,microtime(true)在PHP 4.4中不起作用,因此我必须使用php.net/microtime /示例#1中给出的microtime_float示例。

它对数组元素有影响。

考虑这个例子

$a = array('test' => 1);
$a['test'] = NULL;
echo "Key test ", array_key_exists('test', $a)? "exists": "does not exist";

这里,键“test”仍然存在。然而,在这个例子中

$a = array('test' => 1);
unset($a['test']);
echo "Key test ", array_key_exists('test', $a)? "exists": "does not exist";

密钥不再存在。

郑重声明,不包括花费的时间:

<?php
echo "<hr>First:<br>";
$x = str_repeat('x', 80000);
echo memory_get_usage() . "<br>\n";      
echo memory_get_peak_usage() . "<br>\n"; 
echo "<hr>Unset:<br>";
unset($x);
$x = str_repeat('x', 80000);
echo memory_get_usage() . "<br>\n";      
echo memory_get_peak_usage() . "<br>\n"; 
echo "<hr>Null:<br>";
$x=null;
$x = str_repeat('x', 80000);
echo memory_get_usage() . "<br>\n";      
echo memory_get_peak_usage() . "<br>\n";

echo "<hr>function:<br>";
function test() {
    $x = str_repeat('x', 80000);
}
echo memory_get_usage() . "<br>\n";      
echo memory_get_peak_usage() . "<br>\n"; 

echo "<hr>Reasign:<br>";
$x = str_repeat('x', 80000);
echo memory_get_usage() . "<br>\n";      
echo memory_get_peak_usage() . "<br>\n"; 

它返回

First:
438296
438352
Unset:
438296
438352
Null:
438296
438352
function:
438296
438352
Reasign:
438296
520216 <-- double usage.

结论,空内存和未设置空闲内存都是预期的(不仅在执行结束时)。另外,对变量重新赋值会在某个点上将值保存两次(520216对438352)