商城首页欢迎来到中国正版软件门户

您的位置: 首页 > 文章列表 > 编程开发 > php如何进行垃圾回收,PHP 是如何做垃圾回收的

php如何进行垃圾回收,PHP 是如何做垃圾回收的

  发布于2026-07-20 阅读(0)

扫一扫,手机访问

PHP 的垃圾回收机制,一直是很多开发者感兴趣的话题。特别是从 PHP 5 升级到 PHP 7 之后,变量底层实现和回收策略都发生了不小的变化。今天,我们就来系统地梳理一下,这两个版本在变量存储和垃圾回收上到底有什么不同。

变量的实现

PHP 的变量是弱类型的,一个变量可以表示整数、浮点数、字符串,甚至是数组或对象。这种灵活性,其实得益于底层统一用了一个叫 zval 的结构体来承载。

PHP 5.* 中的 zval 和 zend_value

在 PHP 5 时代,zval 的结构大致是这样的:

struct _zval_struct {
    zvalue_value value;
    zend_uint refcount__gc;
    zend_uchar type;
    zend_uchar is_ref__gc;
}

typedef union _zvalue_value {
    long lval;
    double dval;
    struct {
        char *val;
        int len;
    } str;
    HashTable *ht;
    zend_object_value obj;
    zend_ast *ast;
} zvalue_value;

简单来说,zval 里包含了一个联合体 zvalue_value 来存储实际的值,以及引用计数、类型标记和是否被引用的标记。引用计数和 is_ref 是直接放在 zval 结构体里的。

PHP 7.0 中的 zval 和 zend_value

到了 PHP 7,zval 的结构被大幅重构,变得更紧凑、更高效:

struct _zval_struct {
    union {
        zend_long lval;
        double dval;
        zend_refcounted *counted;
        zend_string *str;
        zend_array *arr;
        zend_object *obj;
        zend_resource *res;
        zend_reference *ref;
        zend_ast_ref *ast;
        zval *zv;
        void *ptr;
        zend_class_entry *ce;
        zend_function *func;
        struct {
            uint32_t w1;
            uint32_t w2;
        } ww;
    } value;
    union {
        struct {
            ZEND_ENDIAN_LOHI_4(
                zend_uchar type,
                zend_uchar type_flags,
                zend_uchar const_flags,
                zend_uchar reserved)
        } v;
        uint32_t type_info;
    } u1;
    union {
        uint32_t var_flags;
        uint32_t next;
        uint32_t cache_slot;
        uint32_t lineno;
        uint32_t num_args;
        uint32_t fe_pos;
        uint32_t fe_iter_idx;
    } u2;
};

可以看到,PHP 7 的 zval 不再直接存储引用计数了,而是把它放到了具体的 value 结构体中。同时,zval 本身不再处理写时复制(写时分离),而是通过一个专门的 zend_reference 结构体来表示引用关系。

PHP5 与 PHP7 引用计数的对比

在 PHP 5 中,变量赋值、函数传参等操作,都会直接操作 zval 里的 refcount__gc。当出现循环引用,比如两个数组互相引用,并且外部变量被 unset 之后,就会形成典型的“垃圾”。

PHP 5 循环引用示例

而 PHP 7 的做法是,把引用计数放到具体的 value 类型中(比如 zend_stringzend_array),zval 本身不再负责计数。这带来的好处是,zval 可以更轻量地传递,写时复制(写时分离)的机制也被简化了。

PHP 7 引用计数结构

理解了这些底层存储细节,我们才能真正理解 PHP 是如何做垃圾回收的。

什么是垃圾

首先,得明确什么才算“垃圾”。

  • 引用计数增加的情况,肯定不是垃圾。
  • 引用计数降到 0 的变量,也不是垃圾——它会被立即回收。
  • 真正需要关注的,是那些引用计数减少,但减少后不等于 0 的变量。这些变量很可能构成了循环引用,成了无法被正常回收的“垃圾”。

垃圾收集

在 PHP 7 中,垃圾收集器只处理两种类型:数组和对象。并且,只有满足以下条件时,才会被送入收集流程:

  • 变量的 type_flag 标记为 IS_TYPE_COLLECTABLE
  • 它还没有在垃圾缓冲区中存在过。
  • 它没有被标记过。
  • 满足条件后,会被标记为紫色,并放入缓冲区。

回收算法

从 PHP 5.3 开始,垃圾回收算法就已经基本定型了。它的核心思路可以概括为以下几个步骤:

  1. 把疑似垃圾的节点放入一个 root 池中。
  2. 当 root 池中的节点数量达到 10000 个时,就会触发一次垃圾回收。
  3. 遍历这个双向链表中的所有节点,把它们的引用计数减 1(模拟断开所有引用)。
  4. 再次遍历,将引用计数为 0 的节点删除,放入 free 队列。
  5. 对于引用计数不为 0 的节点,把引用计数加 1,恢复原状。

垃圾回收算法流程图

这套算法的巧妙之处在于,通过模拟减引用,可以快速识别出那些真正被循环引用“困住”的垃圾,而不会误伤正常的变量。

本文转载于:https://blog.csdn.net/weixin_39701834/article/details/115643752 如有侵犯,请联系zhengruancom@outlook.com删除。
免责声明:正软商城发布此文仅为传递信息,不代表正软商城认同其观点或证实其描述。

热门关注