我意识到第二个方法避免了函数调用的开销(update实际上是一种语言结构),但是知道哪一个更好还是很有趣的。在我的大部分编码中,我一直使用unset()
,但最近我看了一些从网络上找到的受人尊敬的类,它们使用$var = null
代替。
是否有首选的方法,并且有什么理由?
我意识到第二个方法避免了函数调用的开销(update实际上是一种语言结构),但是知道哪一个更好还是很有趣的。在我的大部分编码中,我一直使用unset()
,但最近我看了一些从网络上找到的受人尊敬的类,它们使用$var = null
代替。
是否有首选的方法,并且有什么理由?
在2009年的未设置手册页面中提到:
unset()
正如其名,它用于取消变量的设置。它并不强制立即释放内存。PHP的垃圾回收器会在合适的时候进行释放,有意地在那些CPU周期不再需要的时候,或者在脚本将要耗尽内存之前的最后时刻,以先发生的为准。如果你使用
$whatever = null;
,那么你正在重写变量的数据。这可能会更快地释放/缩小内存,但它可能会从真正需要它们的代码中夺取CPU周期,导致总体执行时间更长。
(自2013年起,unset
手册页面不再包含该部分)
unset($a)
还会从符号表中移除$a
;例如:
$a = str_repeat('hello world ', 100);
unset($a);
var_dump($a);
Outputs:
Notice: Undefined variable: a in xxx
NULL
$a = null
时:$a = str_repeat('hello world ', 100);
$a = null;
var_dump($a);
Outputs:
NULL
$a = null
比 unset()
更快一些:更新符号表条目似乎比删除它更快。
unset
并不是一个函数,而是一种语言结构。它并不像 return
或 include
一样是一个函数调用。
除了性能问题外,使用 unset
可以使您的代码意图更加清晰。
unset
拼写为UnSeT
。社区已经确定使用全小写作为一种风格,但其他大小写仍然有效。 - Mark Reed<?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 结果:
PHP 5.3 结果:
PHP 5.2 结果:
PHP 5.1 结果:
在 PHP 5.0 和 4.4 中情况开始变得不同。
5.0:
4.4:
请注意,在PHP 4.4中microtime(true)无法使用,因此我必须使用php.net/microtime / Example #1中提供的microtime_float示例。
gc_collect_cycles
以获得更准确的结果。 - Pacerier对于通过引用复制的变量,它的工作方式不同:
$a = 5;
$b = &$a;
unset($b); // just say $b should not point to any variable
print $a; // 5
$a = 5;
$b = &$a;
$b = null; // rewrites value of $b (and $a)
print $a; // nothing, because $a = null
数组元素的顺序是有影响的。
考虑以下示例:
$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";
该键不再存在。
关于对象,特别是在懒加载场景下,应该考虑垃圾回收器在空闲CPU周期中运行,因此假设当加载大量对象时会有麻烦,采取小的时间惩罚来解决内存释放问题。
使用time_nanosleep来启用GC以收集内存。将变量设置为null是可取的。
在生产服务器上经过测试,原本任务消耗50MB,然后被停止。使用nanosleep后,内存消耗保持在14MB。
应该说这取决于GC的行为,可能会随着PHP版本的更替而改变。但它在PHP 5.3上运行良好。
例如,这个示例(代码取自VirtueMart2谷歌商店)
for($n=0; $n<count($ids); $n++)
{
//unset($product); //usefull for arrays
$product = null
if( $n % 50 == 0 )
{
// let GC do the memory job
//echo "<mem>" . memory_get_usage() . "</mem>";//$ids[$n];
time_nanosleep(0, 10000000);
}
$product = $productModel->getProductSingle((int)$ids[$n],true, true, true);
...
PHP 7已经解决了这种内存管理问题,并将其减少到最小使用。
<?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";
?>
PHP 7.1 输出:
花费了0.16778993606567秒 花费了0.16630101203918秒
仅供参考,不包括所需时间:
<?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.
结论:无论是null还是unset都能如预期地释放内存(不仅在执行结束时)。此外,重新分配变量在某些时候会将值保存两次(520216与438352)。
示例代码来自评论
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;
在使用php:7.4-fpm
镜像和其他镜像运行的Docker容器中。
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
$whatever
指向一个对象,$whatever = null
会覆盖指针,而不是对象本身,因此它的作用与unset()
基本相同。 - Gras Double