简体   繁体   English

用 PHP 释放 memory 有什么好处: unset() 或 $var = null

[英]What's better at freeing memory with PHP: unset() or $var = null

I realise the second one avoids the overhead of a function call ( update , is actually a language construct), but it would be interesting to know if one is better than the other.我意识到第二个避免了 function 调用的开销( update ,实际上是一种语言结构),但是知道一个是否比另一个更好会很有趣。 I have been using unset() for most of my coding, but I've recently looked through a few respectable classes found off the net that use $var = null instead.在我的大部分编码中,我一直在使用unset() ,但我最近查看了一些在网上找到的可敬的类,它们使用$var = null代替。

Is there a preferred one, and what is the reasoning?有没有首选的,原因是什么?

It was mentioned in the unset manual's page in 2009 : 在 2009 年未设置的手册页中提到了这一点:

unset() does just what its name says - unset a variable. unset()就像它的名字所说的那样 - 取消设置一个变量。 It does not force immediate memory freeing.它不会强制立即释放 memory。 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. PHP 的垃圾收集器会在它认为合适的时候执行它 - 有意尽快,因为无论如何都不需要这些 CPU 周期,或者直到脚本用完 memory 之前,无论先发生什么。

If you are doing $whatever = null;如果你正在做$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.您可能会更快地释放/缩小 memory,但它可能会更快地从真正需要它们的代码中窃取 CPU 周期,从而导致更长的整体执行时间。

(Since 2013, that unset man page don't include that section anymore) (自 2013 年以来,未unset的手册页不再包含该部分)

Note that until php5.3, if you have two objects in circular reference , such as in a parent-child relationship, calling unset() on the parent object will not free the memory used for the parent reference in the child object.请注意,直到 php5.3,如果您在循环引用中有两个对象,例如在父子关系中,则在父 object 上调用 unset() 将不会释放用于子 ZA8CFDE6391149EB2AC966F 中的父引用的 memory。 (Nor will the memory be freed when the parent object is garbage-collected.) ( bug 33595 ) (当父 object 被垃圾收集时,memory 也不会被释放。)(错误 33595


The question " difference between unset and = null " details some differences:问题“ unset and = null 之间的区别”详细说明了一些差异:


unset($a) also removes $a from the symbol table; unset($a)也会从符号表中删除$a for example:例如:

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

Outputs:输出:

Notice: Undefined variable: a in xxx
NULL

But when $a = null is used:但是当使用$a = null时:

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

Outputs:输出:

NULL

It seems that $a = null is a bit faster than its unset() counterpart: updating a symbol table entry appears to be faster than removing it.似乎$a = null比它的unset()对应物快一点:更新符号表条目似乎比删除它更快。


  • when you try to use a non-existent ( unset ) variable, an error will be triggered and the value for the variable expression will be null.当您尝试使用不存在的(未unset )变量时,将触发错误并且变量表达式的值将为 null。 (Because, what else should PHP do? Every expression needs to result in some value.) (因为,PHP 还应该做什么?每个表达式都需要产生一些值。)
  • A variable with null assigned to it is still a perfectly normal variable though.不过,分配有 null 的变量仍然是一个完全正常的变量。

unset is not actually a function, but a language construct . unset实际上不是 function,而是语言结构 It is no more a function call than a return or an include .它不再是 function 调用而不是returninclude

Aside from performance issues, using unset makes your code's intent much clearer.除了性能问题,使用unset可以让你的代码意图更加清晰。

By doing an unset() on a variable, you've essentially marked the variable for 'garbage collection' (PHP doesn't really have one, but for example's sake) so the memory isn't immediately available.通过对变量执行 unset() ,您实际上已将变量标记为“垃圾收集”(PHP 并没有真正的变量,但为了举例说明),因此 memory 不会立即可用。 The variable no longer houses the data, but the stack remains at the larger size.该变量不再包含数据,但堆栈仍保持较大的大小。 Doing the null method drops the data and shrinks the stack memory almost immediately.执行 null 方法几乎立即删除数据并收缩堆栈 memory。

This has been from personal experience and others as well.这既来自个人经验,也来自其他人。 See the comments of the unset() function here .此处查看 unset() function 的注释。

I personally use unset() between iterations in a loop so that I don't have to have the delay of the stack being yo-yo'd in size.我个人在循环中的迭代之间使用 unset() ,这样我就不必延迟堆栈的大小。 The data is gone, but the footprint remains.数据消失了,但足迹依然存在。 On the next iteration, the memory is already being taken by php and thus, quicker to initialize the next variable.在下一次迭代中,memory 已经被 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";
?>

Per that it seems like "= null" is faster.每似乎“= null”更快。

PHP 5.4 results: PHP 5.4 结果:

  • took 0.88389301300049 seconds耗时 0.88389301300049 秒
  • took 2.1757180690765 seconds花了 2.1757180690765 秒

PHP 5.3 results: PHP 5.3 结果:

  • took 1.7235369682312 seconds花了 1.7235369682312 秒
  • took 2.9490959644318 seconds花了 2.9490959644318 秒

PHP 5.2 results: PHP 5.2 结果:

  • took 3.0069220066071 seconds花了 3.0069220066071 秒
  • took 4.7002630233765 seconds花了 4.7002630233765 秒

PHP 5.1 results: PHP 5.1 结果:

  • took 2.6272349357605 seconds花了 2.6272349357605 秒
  • took 5.0403649806976 seconds花了 5.0403649806976 秒

Things start to look different with PHP 5.0 and 4.4. PHP 5.0 和 4.4 的情况开始有所不同。

5.0: 5.0:

  • took 10.038941144943 seconds花了 10.038941144943 秒
  • took 7.0874409675598 seconds花了 7.0874409675598 秒

4.4: 4.4:

  • took 7.5352551937103 seconds花了 7.5352551937103 秒
  • took 6.6245851516724 seconds花了 6.6245851516724 秒

Keep in mind microtime(true) doesn't work in PHP 4.4 so I had to use the microtime_float example given in php.net/microtime / Example #1.请记住 microtime(true) 在 PHP 4.4 中不起作用,因此我不得不使用 php.net/microtime / Example #1 中给出的 microtime_float 示例。

It works in a different way for variables copied by reference:对于通过引用复制的变量,它以不同的方式工作:

$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

It makes a difference with array elements.它与数组元素有所不同。

Consider this example考虑这个例子

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

Here, the key 'test' still exists.在这里,关键的“测试”仍然存在。 However, in this example然而,在这个例子中

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

the key no longer exists.密钥不再存在。

Regarding objects, especially in lazy-load scenario, one should consider garbage collector is running in idle CPU cycles, so presuming you're going into trouble when a lot of objects are loading small time penalty will solve the memory freeing.关于对象,尤其是在延迟加载的情况下,应该考虑垃圾收集器在空闲的 CPU 周期中运行,所以假设你在加载大量对象时遇到了麻烦,小时间惩罚将解决 memory 释放。

Use time_nanosleep to enable GC to collect memory.使用 time_nanosleep 开启 GC 收集 memory。 Setting variable to null is desirable.需要将变量设置为 null。

Tested on production server, originally the job consumed 50MB and then was halted.在生产服务器上测试,最初该作业消耗了 50MB,然后被暂停。 After nanosleep was used 14MB was constant memory consumption.使用 nanosleep 后 14MB 是恒定的 memory 消耗。

One should say this depends on GC behaviour which may change from PHP version to version.应该说这取决于可能从 PHP 版本到版本的 GC 行为。 But it works on PHP 5.3 fine.但它适用于 PHP 5.3 很好。

eg.例如。 this sample (code taken form VirtueMart2 google feed)此示例(代码取自 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);
    ...

I still doubt about this, but I've tried it at my script and I'm using xdebug to know how it will affect my app memory usage.我仍然对此表示怀疑,但我已经在我的脚本中进行了尝试,并且我正在使用 xdebug 来了解它将如何影响我的应用程序 memory 的使用。 The script is set on my function like this:该脚本是在我的 function 上设置的,如下所示:

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;
}

And I add unset just before the return code and it give me: 160200 then I try to change it with $sql = NULL and it give me: 160224:)我在return码之前添加了 unset 它给了我:160200 然后我尝试用$sql = NULL来改变它,它给了我:160224 :)

But there is something unique on this comparative when I am not using unset() or NULL, xdebug give me 160144 as memory usage但是当我不使用 unset() 或 NULL 时,这个比较有一些独特之处,xdebug 给我 160144 作为 memory 用法

So, I think giving line to use unset() or NULL will add process to your application and it will be better to stay origin with your code and decrease the variable that you are using as effective as you can.因此,我认为使用 unset() 或 NULL 会为您的应用程序添加流程,最好保持代码的原始状态并尽可能有效地减少您使用的变量。

Correct me if I'm wrong, thanks如果我错了,请纠正我,谢谢

PHP 7 is already worked on such memory management issues and its reduced up-to minimal usage. PHP 7 已经在解决此类 memory 管理问题,并将其减少到最低限度。

<?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 Outpu: PHP 7.1 输出:

took 0.16778993606567 seconds took 0.16630101203918 seconds耗时 0.16778993606567 秒耗时 0.16630101203918 秒

Code example 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;

Running in docker container from image php:7.4-fpm and others..从映像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

But, with other example:但是,与其他示例:

<?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;

Results:结果:

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

I created a new performance test for unset and =null , because as mentioned in the comments the here written has an error (the recreating of the elements).我为unset=null创建了一个新的性能测试,因为正如评论中提到的,这里写的有一个错误(重新创建元素)。 I used arrays, as you see it didn't matter now.我使用了 arrays,正如您所看到的,现在这并不重要。

<?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>';

But i can only test it on an PHP 5.5.9 server, here the results: - took 4.4571571350098 seconds - took 4.4425978660583 seconds但我只能在 PHP 5.5.9 服务器上对其进行测试,结果如下: - 耗时 4.4571571350098 秒 - 耗时 4.4425978660583 秒

I prefer unset for readability reasons.出于可读性的原因,我更喜欢unset

For the record, and excluding the time that it takes:为了记录,不包括它所花费的时间:

<?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"; 

It returns它返回

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

Conclusion, both null and unset free memory as expected (not only at the end of the execution).结论,null 和未设置的免费 memory 都符合预期(不仅在执行结束时)。 Also, reassigning a variable holds the value twice at some point (520216 versus 438352)此外,重新分配一个变量会在某个时间点两次保存该值(520216 与 438352)

unset code if not freeing immediate memory is still very helpful and would be a good practice to do this each time we pass on code steps before we exit a method.如果不立即释放 memory ,则unset代码仍然非常有用,并且每次我们在退出方法之前传递代码步骤时这样做是一个很好的做法。 take note its not about freeing immediate memory.注意它不是关于立即释放 memory。 immediate memory is for CPU, what about secondary memory which is RAM.立即 memory 用于 CPU,那么辅助 memory 是 RAM。

and this also tackles about preventing memory leaks.这也解决了防止 memory 泄漏的问题。

please see this link http://www.hackingwithphp.com/18/1/11/be-wary-of-garbage-collection-part-2请参阅此链接http://www.hackingwithphp.com/18/1/11/be-wary-of-garbage-collection-part-2

i have been using unset for a long time now.我已经使用 unset 很长时间了。

better practice like this in code to instanly unset all variable that have been used already as array.更好的做法是在代码中立即取消设置已用作数组的所有变量。

$data['tesst']='';
$data['test2']='asdadsa';
....
nth.

and just unset($data);并且just unset($data); to free all variable usage.释放所有变量的使用。

please see related topic to unset请参阅相关主题以取消设置

How important is it to unset variables in PHP? 在 PHP 中取消设置变量有多重要?

[bug] [漏洞]

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM