【PHP】认识PHP的垃圾回收机制(1)

前言

本文的目的不仅仅是总结PHP的垃圾回收机制,而是要发散思维,从垃圾回收机制中得到更多的细节。

什么是垃圾回收

垃圾回收机制广泛存在于各种编程语言中,由于每个变量被定义都会占用一部分内存空间,如果这些空间长期不被释放,系统的内存就会出现不足的情况,为了保证系统能在软件运行过程中有足够的内存使用,就需要这样一套回收机制,释放掉不需要使用的内存空间。

高级语言一般在自动实现了垃圾回收,不需要我们主动去控制。

PHP的垃圾回收实现

php5.3的zval

struct _zval_struct {
     union {
          long lval;
          double dval;
          struct {
               char *val;
               int len;
          } str;
          HashTable *ht;
          zend_object_value obj;
          zend_ast *ast;
     } value;
     zend_uint refcount__gc;
     zend_uchar type;
     zend_uchar is_ref__gc;
};

其中的refcount__gcis_ref__gc负责垃圾回收机制的计数,这里被官方成为"引用计数".

php7的zval

struct _zval_struct {
     union {
          zend_long         lval;             /* long value */
          double            dval;             /* double value */
          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,         /* active type */
                zend_uchar    type_flags,
                zend_uchar    const_flags,
                zend_uchar    reserved)     /* call info for EX(This) */
        } v;
        uint32_t type_info;
    } u1;
    union {
        uint32_t     var_flags;
        uint32_t     next;                 /* hash collision chain */
        uint32_t     cache_slot;           /* literal cache slot */
        uint32_t     lineno;               /* line number (for ast nodes) */
        uint32_t     num_args;             /* arguments number for EX(This) */
        uint32_t     fe_pos;               /* foreach position */
        uint32_t     fe_iter_idx;          /* foreach iterator index */
    } u2;
};

zend_refcounted用来实现垃圾回收。

从PHP7开始, 对于在zval的value字段中能保存下的值, 就不再对他们进行引用计数了, 而是在拷贝的时候直接赋值, 这样就省掉了大量的引用计数相关的操作, 这部分类型有:

  • IS_LONG
  • IS_DOUBLE

对于那种根本没有值, 只有类型的类型, 也不需要引用计数了:

  • IS_NULL
  • IS_FALSE
  • IS_TRUE
    而对于复杂类型, 一个size_t保存不下的, 那么我们就用value来保存一个指针, 这个指针指向这个具体的值, 引用计数也随之作用于这个值上, 而不在是作用于zval上了.
    在这里插入图片描述
    IS_ARRAY的结构如下
struct _zend_array {
    zend_refcounted_h gc;
    union {
        struct {
            ZEND_ENDIAN_LOHI_4(
                zend_uchar    flags,
                zend_uchar    nApplyCount,
                zend_uchar    nIteratorsCount,
                zend_uchar    reserve)
        } v;
        uint32_t flags;
    } u;
    uint32_t          nTableMask;
    Bucket           *arData;
    uint32_t          nNumUsed;
    uint32_t          nNumOfElements;
    uint32_t          nTableSize;
    uint32_t          nInternalPointer;
    zend_long         nNextFreeElement;
    dtor_func_t       pDestructor;
};

其中zend_refcounted_h保存了垃圾回收相关的结构

typedef struct _zend_refcounted_h {
    uint32_t         refcount;          /* reference counter 32-bit */
    union {
        struct {
            ZEND_ENDIAN_LOHI_3(
                zend_uchar    type,
                zend_uchar    flags,    /* used for strings & objects */
                uint16_t      gc_info)  /* keeps GC root number (or 0) and color */
        } v;
        uint32_t type_info;
    } u;
} zend_refcounted_h;

这样,PHP7的垃圾回收是通过refcount来控制的,也是引用计数。

官方文档

官方文档的记录还是php5.2到5.3的垃圾回收机制,基本原理不变,但是控制的变量名称变了。

当引用计数变成0,系统认为变量没有用处,就会被垃圾回收。

在这里插入图片描述

参考资料

  • https://www.laruence.com/2018/04/08/3170.html
  • https://www.php.net/manual/zh/features.gc.refcounting-basics.php
发布了361 篇原创文章 · 获赞 464 · 访问量 174万+

猜你喜欢

转载自blog.csdn.net/diandianxiyu/article/details/104832650