首页 > 解决方案 > PHP 根缓冲区中的内容以及如果它大于 10K 会发生什么

问题描述

我有一个长时间运行的 PHP 脚本,它似乎存在内存泄漏,这让我深入研究了 PHP 垃圾收集的工作原理。我对此有一些疑问,也许这里有些人对内脏有足够的了解来回答这些问题。

首先,我想知道哪些特定变量最终会出现在根缓冲区中。只有顶级项目还是在数组数组中(例如,在查询结果中)该顶级数组中的每个元素都会卡在那里?

输入代码:

$a = [  ['b'=>1234, 'c'=>2345] ];

根缓冲区中只有“a”还是 $a[0] 也在那里?

如果你最终有超过 10K 的根会发生什么?它会在某个时候停止收集吗?

最后,我应该关注 xdebug 垃圾收集报告中的 0% 效率收集吗?

标签: phpphp-internals

解决方案


垃圾收集文档中所述,这些文档非常过时且难以信任:

实现垃圾收集机制的全部原因是通过在满足先决条件后立即清理循环引用的变量来减少内存使用。在 PHP 的实现中,一旦根缓冲区已满,或者调用函数gc_collect_cycles时,就会发生这种情况。

即使您的代码没有内存泄漏,您仍然可能会耗尽内存。最终是先发生什么的问题:

  • GC 已启用(默认)并且满足或调用threshold*了根对象的。gc_collect_cyclesPHP 将收集未引用的对象并释放内存。您的脚本将愉快地运行。
  • 您的脚本内存不足。

[*]我不确定它是如何threshold确定的——它似乎被神奇地设置并且可以随着你的脚本执行而改变。您可以调用gc_status()来查看当前阈值,以及有多少根。

如果您遇到内存不足的问题,因为在达到阈值之前内存就用完了,除了手动调用之外,我不知道如何解决这个问题gc_collect_cycles() 希望有人可以对此有所了解!


现在,你的问题:


根缓冲区中只有“a”还是 $a[0] 也在那里?

引用计数基础文档(搜索Compound Types)中,只会$a将其添加到根缓冲区。这是因为它是一种复合类型(数组或对象)。在文档的图表中,只有虚线内的内容在root buffer- 在这种情况下,是对“a”对象的单个引用。

见下文:

Example #5 创建一个数组 zval

<?php
$a = array( 'meaning' => 'life', 'number' => 42 );
xdebug_debug_zval( 'a' );
?>

上面的示例将输出类似于:

a: (refcount=1, is_ref=0)=array (
   'meaning' => (refcount=1, is_ref=0)='life',
   'number' => (refcount=1, is_ref=0)=42
)

PHP 文档中的图表


如果你最终有超过 10K 的根会发生什么?它会在某个时候停止收集吗?

一旦达到阈值,PHP 就应该进行垃圾回收。

尽管这些文档确实提到了10K根限制,但我认为这对于 PHP 7.3+ 来说并不准确。如前所述,您可以随时运行gc_status()以查看当前阈值。甚至gc_status的文档也显示了 50000 的示例阈值,而不是 10000。

我能够找到一个确认 PHP 运行启发式以确定阈值的 reddit 帖子。它不再固定为 10000。

还有一个错误报告建议更改文档。


最后,我应该关注 xdebug 垃圾收集报告中的 0% 效率收集吗?

来自xdebug 文档

效率% — 清除的根数除以 10000 — 达到的“根”数会触发 PHP 内部垃圾收集器自动运行。

我自己没用xdebug,所以不知道是不是改成当前时间除以阈值了(如果还是用10000的话,有可能得到>100%的效率。)

但是,简而言之,0% 的效率意味着您创建的所有对象/数组都被根级别范围最终引用的东西所引用。据我所知,PHP 可以解析循环引用并清理它们,所以这很可能是内存泄漏。


推荐阅读