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

ale*_*lex 236 php

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

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

Von*_*onC 228

在2009年的未设置手册页面中提到:

unset()正如它的名字所说的那样 - 取消变量.它不会强制立即释放内存.PHP的垃圾收集器会在看到拟合时执行它 - 尽快意图,因为无论如何都不需要那些CPU周期,或者在脚本耗尽内存之前,无论先发生什么.

如果你这样做,$whatever = null;那么你正在重写变量的数据.您可能会更快地释放/缩小内存,但它可能会更快地从真正需要它们的代码中窃取CPU周期,从而导致更长的总体执行时间.

(自2013年起,该unset手册页不再包含该部分)

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


问题" unset和= null之间的区别 "详细说明了一些差异:


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

$a = str_repeat('hello world ', 100);
unset($a);
var_dump($a);
Run Code Online (Sandbox Code Playgroud)

输出:

Notice: Undefined variable: a in xxx
NULL
Run Code Online (Sandbox Code Playgroud)

但是什么时候$a = null使用:

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

NULL
Run Code Online (Sandbox Code Playgroud)

它似乎$a = null比它的unset()对应物快一点:更新符号表条目似乎比删除它更快.


  • 当您尝试使用不存在的(unset)变量时,将触发错误,并且变量表达式的值将为null.(因为,PHP应该做什么?每个表达式都需要产生一些价值.)
  • 赋值为null的变量仍然是一个完全正常的变量.

  • 注意,如果`$ whatever`指向一个对象,`$ whatever = null`会覆盖指针,而不是对象本身,因此它的作用基本上与`unset()`相同. (17认同)
  • @VonC:您所指的 php.net 上的未设置引用不再存在。 (2认同)

Ale*_*ett 46

unset实际上不是一个函数,而是一个语言结构.这是不超过一个函数调用returninclude.

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

  • @alex:PSR-2 [要求](https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-2-coding-style-guide.md#25-keywords-and- truefalsenull)所有关键字的小写字母. (5认同)
  • @alex,你可以用unset来做那件事.例如"$ test = 4;(unset)$ test;" - 奇怪但是真实,它在取消设置之前返回$ test的值.无论如何,PHP手册确实证实它是一种语言结构. (3认同)
  • @alex - PHP关键字不区分大小写; 例如,你也可以拼写`unset`为'UnSeT`.作为一种风格问题,社区已经采用全小写的方式,但其他外壳仍然有效. (2认同)

Wil*_*oyd 35

通过对变量执行unset(),您基本上将变量标记为'垃圾收集'(PHP实际上没有一个,但是例如,为了这个原因),因此内存不会立即可用.变量不再容纳数据,但堆栈仍保持较大的大小.执行null方法会丢弃数据并几乎立即缩小堆栈内存.

这来自个人经验和其他人.请在此处查看unset()函数的注释.

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

  • 如果保持值NULL所需的内存小于保存之前保存的任何值所需的内存,则将某些内容设置为NULL可能会有所帮助.例如,一个长字符串.如果字符串不是常量并且其引用计数降为零,则应释放该内存.取消设置更清晰 - 它不再维护引用.您必须等待垃圾收集,但将其视为不占用内存是安全的,因为低内存条件将触发垃圾收集. (15认同)
  • @NabeelKhan我建议使用unset()内部循环,然后在退出循环时使其无效.否则,在循环内部执行这两个操作会对性能产生影响.如果你没有使用循环,那么只需取消,因为它已经在幕后使用了unset()逻辑. (2认同)

小智 27

<?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";
?>
Run Code Online (Sandbox Code Playgroud)

看来,"= 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"稍微快一点.测试:http://pastebin.com/fUe57C51 (7认同)
  • @ansur,在启动计时器之前始终调用`gc_collect_cycles`以获得更准确的结果. (4认同)

小智 18

它与数组元素有所不同.

考虑这个例子

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

在这里,关键'测试'仍然存在.但是,在这个例子中

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

钥匙不再存在.


Ria*_*iaD 17

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

$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
Run Code Online (Sandbox Code Playgroud)

  • 我已经编写了几年的PHP编程,从未见过关于引用原始var的"&".谢谢+ 1 :) (5认同)

OSP*_*OSP 12

关于对象,特别是在延迟加载场景中,应该考虑垃圾收集器在空闲的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);
    ...
Run Code Online (Sandbox Code Playgroud)


小智 5

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

?>
Run Code Online (Sandbox Code Playgroud)

PHP 7.1 输出:

花了 0.16778993606567 秒 花了 0.16630101203918 秒