前言:我确实知道'unset'在用户区中是如何工作的,但我想知道它是如何在内部工作的。
在zval structure上调用unset时,会减少引用计数器(refcount__gc)。当refcount__gc达到0时,该变量不再使用,可以删除。问题是它是总是立即完成,还是在某些情况下它可以在以后由垃圾收集器完成?
我发现了两个相互矛盾的陈述:
unset()正如它的名字所说 - 取消设置一个变量。它不会强制立即释放内存。 PHP的垃圾收集器会在看到拟合时执行它 - 尽快意图,因为无论如何都不需要那些CPU周期,或者在脚本耗尽内存之前,无论先发生什么。 - Stackoverflow answer mentioning 2009 php.net documentation
恰恰相反:
当refcount达到零时,zval被破坏,它所持有的任何内存现在都是空闲的 - Better Understanding PHP’s Garbage Collection, 2012 article
那么哪一个是正确的,比如PHP 5.3和PHP 5.5?如果可能,也许您可以提供PHP源代码中未设置定义的链接。谢谢!
答案 0 :(得分:5)
这两种说法都是正确的。
让我解释一下。 (至少从PHP 5.0开始就是这样(之前,我不知道)。现在有了phpng,它做了根本性的改变,但是仍然使用了这个原则。)
循环垃圾收集器仅用于循环引用。当两个对象包含彼此的引用时,我们通常会有它们。
在这种情况下,refcount__gc永远不会降到零......在其他地方仍然有一些参考,正常的ZEND_UNSET_ *(星号是ARRAY,OBJ或VAR)不能取消它。所以它必须等待垃圾收集器。
由于性能原因,只定期调用垃圾收集器。
你问过ZEND_UNSET_VAR的定义? http://lxr.php.net/xref/PHP_5_6/Zend/zend_vm_def.h#4069
以下是递减引用等的主要功能:http://lxr.php.net/xref/PHP_5_6/Zend/zend_execute.h#74
因此,如果refcount为零,我们确信没有任何链接到它,我们可以释放它。 (第二个陈述:只是在谈论refcount == 0 case)
但是,如果它不为零,我们将变量标记为稍后由循环垃圾收集器检查。 (第一个陈述:不一定立即释放)