PHP 的“未设置”构造如何在内部工作?

Posted

技术标签:

【中文标题】PHP 的“未设置”构造如何在内部工作?【英文标题】:How does PHP's 'unset' construct work internally? 【发布时间】:2014-08-24 19:53:14 【问题描述】:

前言:我确实知道“未设置”在用户空间中是如何工作的,但我想了解它在内部是如何工作的。

当在zval structure 上调用 unset 时,它会减少引用计数器 (refcount__gc)。当 refcount__gc 达到 0 时,该变量不再使用,可以删除。问题是它是总是立即完成,还是在某些情况下可以由垃圾收集器稍后完成?

我发现了两个相互矛盾的陈述:

unset() 就像它的名字所说的那样 - 取消设置一个变量。它不会强制立即释放内存。 php 的垃圾收集器将在它认为合适的时候执行它 - 有意尽快,因为无论如何都不需要这些 CPU 周期,或者直到脚本耗尽内存之前,无论首先发生什么。 - *** answer mentioning 2009 php.net documentation

反之:

当 refcount 达到零时,zval 被销毁,它所持有的任何内存现在都是空闲的 - Better Understanding PHP’s Garbage Collection, 2012 article

那么,假设 PHP 5.3 和 PHP 5.5,哪一个是正确的?如果可能的话,也许您可​​以提供指向 PHP 源代码中未设置定义的链接。谢谢!

【问题讨论】:

【参考方案1】:

TL;DR

这两种说法都是正确的。

让我解释一下。 (至少从 PHP 5.0 开始是这样(之前,我不知道)。现在有 phpng,它做了根本性的改变,但仍然使用这个原理。)


循环垃圾收集器

循环垃圾收集器仅用于循环引用。我们通常在两个对象包含相互引用时使用它们。

因为在这种情况下 refcount__gc 永远不会降为零……在其他地方仍有一些引用,正常的 ZEND_UNSET_*(其中星号是 ARRAY、OBJ 或 VAR)无法取消设置它。所以它必须等待垃圾收集器。

并且垃圾收集器仅出于性能原因定期调用。

php-src 定义

您询问了 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 的情况)

但是,如果它不为零,我们将变量标记为稍后由循环垃圾收集器检查。 (第一句话:不一定立即释放)

【讨论】:

感谢您的详细回答,基本上我在寻找递减的引用计数和释放内存逻辑,所以这是您提供的第二个链接。据我了解,这些是lxr.php.net/xref/PHP_5_6/Zend/zend_execute.h#74 中的第 76-80 行,用于检查 refcount == 0,并在 efree_rel(zval_ptr); 中释放内存;对吗?

以上是关于PHP 的“未设置”构造如何在内部工作?的主要内容,如果未能解决你的问题,请参考以下文章

SignalR 如何在内部工作?

PHP 的内置函数是如何在内部实现的?

SqlDataAdapter 如何在内部工作?

数据库如何在内部工作? [关闭]

新的 FragmentTransaction commitNow() 在内部如何工作?

数据库提交如何在内部工作