美文网首页
PHP7垃圾回收机制

PHP7垃圾回收机制

作者: 十年磨一剑1111 | 来源:发表于2020-05-28 16:15 被阅读0次

前面一篇文章我们聊了下PHP5的垃圾回收机制,本篇文章再来聊下PHP7的垃圾回收机制。PHP7的速度比PHP5快了2~3倍,性能提升了不少,了解一些底层一点的东西有助于我们理解它的性能为什么会提示几倍的原因。同样的,我们要了解PHP7的垃圾回收机制,必须先得了解zval 结构。下面的源码版本是php7.2.31。

一、 几个重要的数据结构

  1. zval 结构
typedef struct _zval_struct     zval;
struct _zval_struct {
    zend_value        value;            /* 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     next;                 /* 用于解决hash冲突,有冲突的key的value放入到一个链表中 */
        uint32_t     cache_slot;           /* 缓存槽(用于运行时缓存) */
        uint32_t     lineno;               /* 标记该变量对应php行号,用于AST(抽象语法树)节点*/
        uint32_t     num_args;             /* 函数调用时的传参个数 */
        uint32_t     fe_pos;               /* foreach循环位置,循环每移动一次,该值加1 */
        uint32_t     fe_iter_idx;          /* foreach循环中的游标位置 */
        uint32_t     access_flags;         /* 类常量访问标志 */
        uint32_t     property_guard;       /* 防止类中的魔术方法的循环引用,get和set中会用到*/
        uint32_t     extra;                /* 用于扩展的字段,暂时还没有明确的说明 */
    } u2;
};
  1. zend_value 结构
typedef union _zend_value {
   zend_long         lval;//整形
   double            dval;//浮点型
   zend_refcounted  *counted;//获取不同类型的gc头部
   zend_string      *str;//string字符串
   zend_array       *arr;//数组
   zend_object      *obj;//对象
   zend_resource    *res;//资源
   zend_reference   *ref;//是否是引用类型
   zend_ast_ref     *ast;  //抽象语法树
   zval             *zv;         // zval类型
   void             *ptr;        //指针
   zend_class_entry *ce;   // 类
   zend_function    *func;   //函数
   struct {
            ZEND_ENDIAN_LOHI(
            uint32_t w1,
            uint32_t w2)
  } ww;
} zend_value;
  1. zend_refcounted 引用计数结构
struct _zend_refcounted {
    zend_refcounted_h gc;
};
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;
  1. zend_reference结构(是否为引用标志)
struct _zend_reference {
    zend_refcounted_h gc;
    zval              val;
};

二、 几个重要的变量

下面先来介绍下zval,zend_value 中几个重要的变量
(1)在zval中 zend_uchar type 这个是用来表示当前变量是什么类型的变量,php7中的zval的类型做了比较大的调整,总体来说有如下19种类型:

/* regular data types */
#define IS_UNDEF                    0
#define IS_NULL                     1
#define IS_FALSE                    2
#define IS_TRUE                     3
#define IS_LONG                     4
#define IS_DOUBLE                   5
#define IS_STRING                   6
#define IS_ARRAY                    7
#define IS_OBJECT                   8
#define IS_RESOURCE                 9
#define IS_REFERENCE                10

/* constant expressions */
#define IS_CONSTANT                 11
#define IS_CONSTANT_AST             12

/* fake types */
#define _IS_BOOL                    13
#define IS_CALLABLE                 14
#define IS_ITERABLE                 19
#define IS_VOID                     18

/* internal types */
#define IS_INDIRECT                 15
#define IS_PTR                      17
#define _IS_ERROR                   20

其中php5的时候is_bool类型,现在拆分成了is_false和is_true两种类型,而原来的引用是一个标志位,现在的引用是一种新的类型。
对于IS_INDIRECT和IS_PTR来说,这两个类型是用在内部的保留类型,用户不会感知。

(2)在zend_value 中 zend_refcounted *counted; 表示引用计数的次数,就是zend_value变量被zval引用的次数,下面举几个例子:

$a = 123;

xdebug_debug_zval('a');

php7 输出结果:

a: (refcount=0, is_ref=0)=123

php5输出结果:

a: (refcount=1, is_ref=0)=123

分析:首先$a不是引用变量,所以is_ref都是0,不同的是在php7中refcount=0,而在php5中refcount=1,由于变量a是一个整型变量,在php中变量a的值被直接保存在了zend_value.lval字段里面,并且在php7中refcount是在zend_value结构里面的,所以在php7中不需要引用计数;在php5中refcount字段是放在zval结构里面的,zend_value被zval引用一次,所以这里是1。

三、php7 变量的内部实现

php7中变量的实现分以下几种方式
(1) 对于boolean类型,还有null,undefined,这种没有具体值,只有类型的的类型,直接在zval中通过zend_uchar type的类型来判断,无需通过引用计数来实现。


zval.png

正是因为没有通过引用计数来实现,所以它refcount为0。比如:

$a = true;
xdebug_debug_zval('a');
$b= $a;
xdebug_debug_zval('b');
$c=$a;
xdebug_debug_zval('c');

输出结果:
a: (refcount=0, is_ref=0)=TRUE
b: (refcount=0, is_ref=0)=TRUE
c: (refcount=0, is_ref=0)=TRUE
我们可以看到这三个变量的refcount的都是0,
(2) 对于int类型和float类型,因为在zend_value中有zend_long和double来保存数据,所以在赋值的时候就不需要再使用引用计数了,在拷贝的时候直接进行赋值就行了,这样做可以省掉大量的引用计数的相关操作。

typedef union _zend_value {
   zend_long         lval;//整形
   double            dval;//浮点型
  ....

定义一个$a=1,在php内核中zval和zval_value的关系:


zval.png

比如:

$a = 1;
xdebug_debug_zval('a');
$b=$a;
xdebug_debug_zval('b');
$c=$a;
xdebug_debug_zval('c');

输出结果:
a: (refcount=0, is_ref=0)=1
b: (refcount=0, is_ref=0)=1
c: (refcount=0, is_ref=0)=1
也就是说在赋值的时候是直接进行赋值操作,而不是通过改变引用计数的方式,说得直接点就是php7中普赋值操作是复制出多个zval结构出来,大家可以发挥下想象吧。

(3) 第三种就是常规的使用引用计数的方式来进行变量的定义。在这些变量实现中,都是通过指针指向一个具体的数据类型。比如字符串类型:

zend_string *str;
struct _zend_string {
     zend_refcount_h gc;
     zend_ulong   h;    /*hash value*/
     size_t            len;
     char   val[1];
}

举个例子:

$a = 'aaa';
xdebug_debug_zval('a');

输出结果:
a: (interned, is_ref=0)='aaaa'
这里的interned 表示这个一个内部字符串(函数名,类名,变量名,静态字符串),对于内部字符串而言,字符串的内容是唯一不变的,它们在生存周期存在于整个请求期间,request完后会统一销毁释放,自然也就无需通过引用计数进行内存的管理。下面在来看下其他形式的字符串。

$a = 'test'.time();
xdebug_debug_zval('a');

输出结果:

a: (refcount=1, is_ref=0)='aaaa1590654681

这种类型的字符串变量就会使用到引用计数。这是一个动态的字符串,这个时候就会使用zend_value中zend_string *str 字段就会保存一个指向zend_string结构的指针:
下面来看下引用,在了解引用之前我们先来回顾下zend_reference结构:

struct _zend_reference{  
zend_refcounted_h gc;  
zval       val;  
} 

当一个变量被引用时或者说是一个引用变量的时候,php会做如下操作:

  1. php首先会申请一个zend_reference结构
  2. 将zend_
$var_int_1 = 233;  
$var_int_2 = &$var_int_1;  
xdebug_debug_zval('var_int_1');  

输出结果:

var_int_1: (refcount=2, is_ref=1)=233

未完,待续。。

相关文章

  • PHP7垃圾回收机制

    前面一篇文章我们聊了下PHP5的垃圾回收机制,本篇文章再来聊下PHP7的垃圾回收机制。PHP7的速度比PHP5快了...

  • PHP的面试总结

    PHP的垃圾回收机制是什么? php5和php7的垃圾回收机制都是利用引用计数。 在zval结构体中定义了ref_...

  • JavaScript的垃圾回收机制

    大纲 1、认识垃圾回收机制2、垃圾回收机制的原理3、垃圾回收机制的标记策略4、垃圾回收机制与内存管理 1、认识垃圾...

  • JS垃圾回收机制(内存问题)

    垃圾收集机制理解 垃圾收集机制:释放无用的数据,回收内存。 垃圾收集机制分类:1)自动回收 2)手动回收 垃圾收集...

  • JVM垃圾回收机制

    JVM垃圾回收 整体思维导图 带着问题理解JVM垃圾回收机制 Java为什么需要垃圾回收机制; 回收哪部分垃圾; ...

  • 垃圾回收机制

    垃圾回收机制—GC Javascript具有自动垃圾回收机制(GC:Garbage Collecation),也就...

  • javascript的垃圾回收机制与内存管理

    一、垃圾回收机制—GCJavaScript具有自动垃圾回收机制(GC:Garbage Collecation),也...

  • Java垃圾回收详解

    深入理解 Java 垃圾回收机制 深入理解 Java 垃圾回收机制 一:垃圾回收机制的意义 java 语言中一个...

  • php7垃圾回收机制

    0. gc的基本结构 0.1 zend_refcounted_h 在《php7的引用计数》一文中,我们说过,php...

  • 常见垃圾回收算法

    什么是垃圾回收(GC) 垃圾回收机制是一种找到内存中的垃圾将其释放并回收空间的机制 常见的垃圾回收算法 引用计数法...

网友评论

      本文标题:PHP7垃圾回收机制

      本文链接:https://www.haomeiwen.com/subject/vhwiahtx.html