【发布时间】:2010-10-09 18:08:24
【问题描述】:
我意识到第二个避免了函数调用的开销(update,实际上是一种语言结构),但是知道一个是否比另一个更好会很有趣。在我的大部分编码中,我一直使用unset(),但我最近浏览了一些在网上找到的使用$var = null 的可敬类。
是否有首选,原因是什么?
【问题讨论】:
标签: php
我意识到第二个避免了函数调用的开销(update,实际上是一种语言结构),但是知道一个是否比另一个更好会很有趣。在我的大部分编码中,我一直使用unset(),但我最近浏览了一些在网上找到的使用$var = null 的可敬类。
是否有首选,原因是什么?
【问题讨论】:
标签: php
unset manual's page in 2009中提到过:
unset()就像它的名字所说的那样——取消设置一个变量。它不会强制立即释放内存。 PHP 的垃圾收集器会在它认为合适的时候执行它 - 有意尽快,因为无论如何都不需要这些 CPU 周期,或者直到脚本耗尽内存之前,无论先发生什么。如果您正在执行
$whatever = null;,那么您正在重写变量的数据。您可能会更快地释放/缩小内存,但它可能会更快地从真正需要它们的代码中窃取 CPU 周期,从而导致整体执行时间更长。
(自 2013 年以来,unset man page 不再包含该部分)
注意,直到 php5.3,如果你有two objects in circular reference,比如在父子关系中,在父对象上调用 unset() 将不会释放子对象中用于父引用的内存。 (当父对象被垃圾回收时,内存也不会被释放。)(bug 33595)
问题“difference between unset and = 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()快一点:更新符号表条目似乎比删除它更快。
unset) 变量时,将触发错误并且变量表达式的值将为空。 (因为,PHP 还应该做什么?每个表达式都需要产生一些值。)【讨论】:
$whatever指向一个对象,$whatever = null会覆盖指针,而不是对象本身,所以它的作用与unset()基本相同。
unset 实际上不是一个函数,而是一个语言结构。它只是一个函数调用,而不是 return 或 include。
除了性能问题,使用unset 可以让您的代码意图更加清晰。
【讨论】:
unset 拼写为UnSeT。社区已将全小写作为风格问题,但其他大小写仍然有效。
通过对变量执行 unset(),您实际上已将变量标记为“垃圾收集”(PHP 并没有真正的,但为了举例说明),因此内存不会立即可用。该变量不再包含数据,但堆栈仍保持较大的大小。执行 null 方法几乎立即删除数据并缩小堆栈内存。
这来自个人经验和其他人的经验。查看 unset() 函数的 cmets here。
我个人在循环中的迭代之间使用 unset(),这样我就不必延迟堆栈的大小。数据消失了,但足迹依然存在。在下一次迭代中,内存已经被 php 占用,因此可以更快地初始化下一个变量。
【讨论】:
<?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:
记住 microtime(true) 在 PHP 4.4 中不起作用,所以我不得不使用 php.net/microtime / Example #1 中给出的 microtime_float 示例。
【讨论】:
unset 重做测试,则速度会更快。我有一个测试,稍后会检查 unset 案例中是否存在。在该测试中,将其设置为 null 会稍微快一些。测试:pastebin.com/fUe57C51
gc_collect_cycles 以获得更准确的结果。
对于通过引用复制的变量,它以不同的方式工作:
$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";
在这里,关键的“测试”仍然存在。然而,在这个例子中
$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 google feed)
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);
...
【讨论】:
我仍然对此表示怀疑,但我已经在我的脚本中进行了尝试,并且我正在使用 xdebug 来了解它将如何影响我的应用程序内存使用情况。 脚本是这样设置在我的函数上的:
function gen_table_data($serv, $coorp, $type, $showSql = FALSE, $table = 'ireg_idnts') {
$sql = "SELECT COUNT(`operator`) `operator` FROM $table WHERE $serv = '$coorp'";
if($showSql === FALSE) {
$sql = mysql_query($sql) or die(mysql_error());
$data = mysql_fetch_array($sql);
return $data[0];
} else echo $sql;
}
我在 return 代码之前添加了 unset,它给了我:160200
然后我尝试用$sql = NULL 更改它,它给了我:160224 :)
但是当我不使用 unset() 或 NULL 时,这个比较有一些独特之处,xdebug 给我 160144 作为内存使用情况
所以,我认为使用 unset() 或 NULL 会为您的应用程序添加进程,最好保持代码的原始性并尽可能有效地减少您正在使用的变量。
如有错误请指正,谢谢
【讨论】:
记录在案,不包括所花费的时间:
<?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 和未设置的空闲内存都符合预期(不仅在执行结束时)。此外,重新分配变量会在某个时间点将值保留两次(520216 与 438352)
【讨论】:
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 秒
【讨论】:
代码示例from comment
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 容器 中从图像 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
【讨论】:
我为unset 和=null 创建了一个新的性能测试,因为正如cmets 中提到的,这里写的有一个错误(重新创建元素)。
我使用了数组,你现在看到它没关系了。
<?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 秒
出于可读性原因,我更喜欢unset。
【讨论】:
unset 代码如果不释放即时内存仍然非常有用,并且每次我们在退出方法之前传递代码步骤时这样做是一个很好的做法。注意它不是关于释放即时内存。
即时内存是用于 CPU 的,而辅助内存是 RAM。
这也解决了防止内存泄漏的问题。
请看这个链接 http://www.hackingwithphp.com/18/1/11/be-wary-of-garbage-collection-part-2
我已经使用 unset 很长时间了。
在代码中更好的做法是立即取消设置已用作数组的所有变量。
$data['tesst']='';
$data['test2']='asdadsa';
....
nth.
和just unset($data); 释放所有变量的使用。
请参阅相关主题以取消设置
How important is it to unset variables in PHP?
[错误]
【讨论】: