当前位置: 首页 > 后端技术 > PHP

PHP回收循环

时间:2023-03-29 21:01:22 PHP

原文:收集循环(CollectingCycles),在最下面加一篇论文。以下过程仅适用于数组和对象类型。传统上,PHP过去使用的引用计数内存机制无法处理循环引用内存泄漏。然而,PHP5.3.0使用文章?引用计数系统中的并发循环集合中的同步算法来处理此内存泄漏。对该算法的完整描述有点超出本节的范围,这里只介绍基础知识。首先,我们要建立一些基本规则,如果一个引用计数增加了,它就会继续被使用,当然也就不再是垃圾了。如果引用计数减少到零,变量容器将被清除(释放)。也就是说,仅当引用计数减少到非零值时才会发生垃圾循环。第二,在一次垃圾循环中,通过检查引用计数是否减1,检查哪些变量容器的引用计数为零,从而找出哪一部分是垃圾。为了避免必须检查所有引用计数可能减少的垃圾循环,该算法将所有可能的根(可能的根是zval变量容器)放入根缓冲区(标记为紫色,称为可疑垃圾),同时确保每个可能的垃圾根在缓冲区中只出现一次。只有当根缓冲区已满时,才会对根缓冲区内的所有不同变量容器执行垃圾收集。请参见上图中的步骤A。在步骤B中,模拟删除每个紫色变量。模拟删除时,可以将非紫色的普通变量的引用计数减“1”。如果一个普通变量的引用计数变为0,再对这个普通变量进行一次模拟删除。每个变量只能模拟删除一次,模拟删除后会标记为灰色(原文说保证同一个变量容器不会被减“1”两次,这是错误的)。在步骤C中,模拟恢复每个紫色变量。恢复是有条件的。当变量的引用计数大于0时,会被模拟恢复。同理,每个变量只能恢复一次,恢复后会被标记为黑色,基本上就是步骤B的逆操作。这样,剩下不能恢复的蓝色节点就是应该恢复的蓝色节点被删除,在步骤D中遍历并删除。算法中有模拟删除、模拟恢复、真实删除,均采用简单遍历(最典型的深度搜索遍历)。复杂度与执行模拟操作的节点数量呈正相关,而不仅仅是那些紫色的疑似垃圾变量。现在您对该算法有了基本的了解,让我们回头看看它是如何与PHP集成的。默认情况下,PHP的垃圾回收机制是开启的,并且有一个php.ini设置允许你修改它:zend.enable_gc。开启垃圾回收机制后,每当根缓存满时,就会执行上述的循环搜索算法。根缓冲区具有固定大小,可以存储10,000个可能的根。当然,你可以通过修改PHP源文件Zend/zend_gc.c中的常量GC_ROOT_BUFFER_MAX_ENTRIES,然后重新编译PHP来修改10000的值。当禁用垃圾收集时,循环搜索算法永远不会执行,但是,可能的根将始终存在于根缓冲区中,无论配置中是否启用垃圾收集。当关闭垃圾收集机制时,如果根缓冲区已经装满了可能的根,那么显然不会记录更多的可能根。那些没有被记录的可能根将不会被该算法分析和处理。如果它们是循环引用循环的一部分,它们将永远不会被清除并导致内存泄漏。即使在垃圾收集不可用时也会记录可能的根的原因是记录可能的根比每次找到可能的根时检查垃圾收集是否开启要快。然而,垃圾收集和分析机制本身需要花费大量时间。除了修改配置zend.enable_gc外,还可以分别调用gc_enable()和gc_disable()函数来开启和关闭垃圾回收机制。调用这些函数与修改配置项开启或关闭垃圾回收机制效果相同。即使根缓冲区可能尚未满,也强制定期回收。您可以为此调用gc_collect_cycles()函数。此函数将返回使用此算法回收的循环数。允许打开和关闭垃圾收集并允许自主初始化的原因是因为应用程序的某些部分可能对时间敏感。在这种情况下,您可能不想使用垃圾回收。当然,关闭部分应用程序的垃圾收集可能会带来内存泄漏的风险,因为某些可能的根可能不适合有限的根缓冲区。因此,在调用gc_disable()释放内存之前调用gc_collect_cycles()可能是明智的。因为这会清除所有已经存储在根缓冲区中的可能根,然后当关闭垃圾收集时,可以留下一个空缓冲区,为可能的根留出更多空间。引用计数系统中的同步循环集合(ConcurrentCycleCollection)