【问题标题】:What's better at freeing memory with PHP: unset() or $var = null用 PHP 释放内存有什么好处:unset() 或 $var = null
【发布时间】:2010-10-09 18:08:24
【问题描述】:

我意识到第二个避免了函数调用的开销(update,实际上是一种语言结构),但是知道一个是否比另一个更好会很有趣。在我的大部分编码中,我一直使用unset(),但我最近浏览了一些在网上找到的使用$var = null 的可敬类。

是否有首选,原因是什么?

【问题讨论】:

    标签: php


    【解决方案1】:

    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 还应该做什么?每个表达式都需要产生一些值。)
    • 尽管分配了 null 的变量仍然是一个完全正常的变量。

    【讨论】:

    • 注意,如果$whatever指向一个对象,$whatever = null会覆盖指针,而不是对象本身,所以它的作用与unset()基本相同。
    • @VonC:您所指的 php.net 上未设置的报价已不存在。
    • @JürgenThelen 是的,但是那个旧答案的内容似乎仍然相关,不是吗?
    • @VonC:当然。我只是不确定“不需要 CPU 周期”和“在 .. 内存不足之前”会触发垃圾收集。见stackoverflow.com/q/20230626/693207。或许你能解释一下?
    • @Omar 我已经编辑了答案:2009 年未设置的手册页(我已链接到 2009 版本)确实包含同一页面的当前版本中不再存在的部分。
    【解决方案2】:

    unset 实际上不是一个函数,而是一个语言结构。它只是一个函数调用,而不是 returninclude

    除了性能问题,使用unset 可以让您的代码意图更加清晰。

    【讨论】:

    • @VonC:是的,我想到了,但是为什么你可以使用小写的 true、false 和 null?
    • @alex,你可以通过取消设置来做到这一点。例如“$test = 4;(未设置)$test;” - 奇怪但真实,它在取消设置之前返回 $test 的值。无论如何,PHP 手册确实确认它是一种语言结构。
    • @alex: PSR-2 requires 所有关键字都小写。
    • @alex - PHP 关键字不区分大小写;例如,您也可以将unset 拼写为UnSeT。社区已将全小写作为风格问题,但其他大小写仍然有效。
    • @Tgr,PHP 就是 PHP。 PSR 就是 PSR。
    【解决方案3】:

    通过对变量执行 unset(),您实际上已将变量标记为“垃圾收集”(PHP 并没有真正的,但为了举例说明),因此内存不会立即可用。该变量不再包含数据,但堆栈仍保持较大的大小。执行 null 方法几乎立即删除数据并缩小堆栈内存。

    这来自个人经验和其他人的经验。查看 unset() 函数的 cmets here

    我个人在循环中的迭代之间使用 unset(),这样我就不必延迟堆栈的大小。数据消失了,但足迹依然存在。在下一次迭代中,内存已经被 php 占用,因此可以更快地初始化下一个变量。

    【讨论】:

    • 如果保存值 NULL 所需的内存小于保存之前保存的任何值所需的内存,则将某些内容设置为 NULL 可能会有所帮助。例如,一个长字符串。如果字符串不是常量并且它的引用计数下降到零,那么应该释放该内存。 Unset 更干净 - 它不再维护参考。您确实必须等待垃圾回收,但将其视为不占用内存是安全的,因为内存不足会触发垃圾回收。
    • 我们不能同时使用吗?等于 null 然后取消设置?
    • @NabeelKhan 我建议在循环内使用 unset() ,然后在退出循环时将其无效。否则,在循环内执行这两项操作都会对性能产生影响。如果您不使用循环,则只需取消它,因为它已经在幕后执行了 unset() 逻辑。
    【解决方案4】:
    <?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 / Example #1 中给出的 microtime_float 示例。

    【讨论】:

    • 我认为你的测试有缺陷。第一个循环是简单的重新分配,第二个循环破坏并重新创建相同的符号。如果使用数组unset 重做测试,则速度会更快。我有一个测试,稍后会检查 unset 案例中是否存在。在该测试中,将其设置为 null 会稍微快一些。测试:pastebin.com/fUe57C51
    • @ansur,请务必在启动计时器之前致电gc_collect_cycles 以获得更准确的结果。
    • @knyri 你能链接到那个吗?
    • @NabeelKhan 我不再有那个测试的结果;但在我之前的评论中有一个测试代码的链接。
    • 使用 PHP 8 我得到以下结果:耗时 0.13596701622009 秒,耗时 0.12726712226868 秒。所以 unset 稍微快一点。
    【解决方案5】:

    对于通过引用复制的变量,它以不同的方式工作:

    $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
    

    【讨论】:

    • 我已经编码 php 几年了,从来没有见过关于引用原始 var 的“&”。谢谢 + 1 :)
    • $a=78; $b=$a;未设置($a); var_dump($b);//78; var_dump($a);//未定义变量:a
    【解决方案6】:

    数组元素会有所不同。

    考虑这个例子

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

    密钥不再存在。

    【讨论】:

      【解决方案7】:

      关于对象,特别是在延迟加载的情况下,应该考虑垃圾收集器在空闲的 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);
          ...
      

      【讨论】:

        【解决方案8】:

        我仍然对此表示怀疑,但我已经在我的脚本中进行了尝试,并且我正在使用 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 会为您的应用程序添加进程,最好保持代码的原始性并尽可能有效地减少您正在使用的变量。

        如有错误请指正,谢谢

        【讨论】:

        【解决方案9】:

        记录在案,不包括所花费的时间:

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

        【讨论】:

          【解决方案10】:

          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 秒

          【讨论】:

            【解决方案11】:

            代码示例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
            
            

            【讨论】:

              【解决方案12】:

              我为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

              【讨论】:

                【解决方案13】:

                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?

                [错误]

                【讨论】:

                  猜你喜欢
                  • 1970-01-01
                  • 2013-08-09
                  • 2012-07-27
                  • 2010-11-23
                  • 1970-01-01
                  • 2012-07-08
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  相关资源
                  最近更新 更多