Home  >  Article  >  Backend Development  >  PHP source code analysis - reference counting & copy-on-write of variables (Reference counting & Copy-on-Wr_PHP tutorial

PHP source code analysis - reference counting & copy-on-write of variables (Reference counting & Copy-on-Wr_PHP tutorial

WBOY
WBOYOriginal
2016-07-13 17:47:231215browse

There are two assignment methods in PHP syntax: reference assignment and non-reference assignment.

$a = 1;

$b = $a; // Non-reference assignment

$c = &$b; // Reference assignment

        $a = 1;

         $b = $a; // Non-reference assignment

         $c = &$b; // Reference assignment

From the surface, it is usually thought like this: "Reference assignment means that two variables correspond to the same variable (actually a zval in C), while non-reference assignment means a new variable (zval) is directly generated, and the value is copied at the same time. come over".

This kind of thinking is understandable in most cases. (#1)

But in some cases it will be very inefficient, such as: (#2)

function print_arr($arr){//Non-reference passing

Print_r($arr);

}

$test_arr = array(

        'a' =>

        'b' => 'b',

        'c' => 'c',

               ...

);//Here is a relatively large array

print_arr($test_arr);//The first time the print_arr function is called to execute the output

print_arr($test_arr);//Calling the print_arr function for the second time to perform output

function print_arr($arr){//Non-reference transfer

          print_r($arr);

}

$test_arr = array(

              'a'   => 'a',

               'b'   =>

               'c'   => 'c',

                     ...

);//Here is a relatively large array

print_arr($test_arr);//The first time the print_arr function is called to execute the output

print_arr($test_arr);//The second time the print_arr function is called to execute the output

If you follow the above understanding (#1) and execute print_arr twice, and in a non-reference manner, two new variables that are exactly the same as $test_arr will be generated, which will be very inefficient.

When the actual code is running, two new variables will not be generated. Because the PHP kernel has already helped us optimize it.

How is this achieved? Here we come to the main points of this article: Reference counting & Copy-on-Write, which is optimized by using the two mechanisms of reference counting and copy-on-write.

Before introducing these two mechanisms, let's first understand a basic knowledge: how variables in PHP are represented in the kernel.

Variables defined in PHP are represented by a zval. The definition of zval is defined in Zend/zend.h:

typedef struct _zval_struct zval;

typedef union _zvalue_value {

long lval; /* long value */

double dval; /* double value */

struct {

char *val;

int len;

} str;

HashTable *ht; /* hash table value */

zend_object_value obj;

} zvalue_value;

struct _zval_struct {

/* Variable information */

zvalue_value value; /* value */

zend_uint refcount;

zend_uchar type; /* active type */

    zend_uchar is_ref; 

}; 

typedef struct _zval_struct zval; 

 

typedef union _zvalue_value {

    long lval;                  /* long value */

    double dval;                /* double value */

    struct {

        char *val;

        int len;

    } str;

    HashTable *ht;              /* hash table value */

    zend_object_value obj;

} zvalue_value;

 

struct _zval_struct {

    /* Variable information */

    zvalue_value value;     /* value */

    zend_uint refcount;

    zend_uchar type;    /* active type */

    zend_uchar is_ref;

};

 

其中,refcount和is_ref就是实现引用计数、写时复制这两个机制的基础。

refcount当前变量存储引用计数,在zval初始创建的时候就为1。每增加一个引用,则refcount ++。当进行引用分离时,refcount--。

is_ref用于表示一个zval是否是引用状态。zval初始化的情况下会是0,表示不是引用。

 

 

 

 

 

 

$a;//a:refcount=1,is_ref=0, value=NULL;  

$a = 1; //a:refcount=2,is_ref=0, value=1;  

$b = $a;    //a,b:refcount=3,is_ref=0,value=1;  

$c = $a;    //a,b,c:refcount=4,is_ref=0,value=1;  

$d = &$c; //a,b:refcount=3,is_ref=0,value=1;    c,d:refcount=1, is_ref=1, value=1 

$a;//a:refcount=1,is_ref=0, value=NULL;

$a = 1;    //a:refcount=2,is_ref=0, value=1;

$b = $a;  //a,b:refcount=3,is_ref=0,value=1;

$c = $a;  //a,b,c:refcount=4,is_ref=0,value=1;

$d = &$c; //a,b:refcount=3,is_ref=0,value=1;      c,d:refcount=1, is_ref=1, value=1上面代码的注释,表示当执行这一行后,refcount与is_ref的变化.

 

 

 

 

Copy on Write

 

 

Php变量通过引用计数实现变量共享数据,那如果改变其中一个变量值呢?

 

当试图写入一个变量时,Zend若发现该变量指向的zval被多个变量共享,则为其复制一份ref_count为1的zval,并递减原zval的refcount,这个过程称为“zval分离”。可见,只有在有写操作发生时zend才进行拷贝操作,因此也叫copy-on-write(写时拷贝)

 

对于引用型变量,其要求和非引用型相反,引用赋值的变量间必须是捆绑的,修改一个变量就修改了所有捆绑变量。

 

 

    $a=1; 

    $b=$a; 

       $a=1;

       $b=$a;执行过程中的内存结构图:

 

 

 

    $a=1; 

    $b=&a; 

       $a=1;

       $b=&a;执行过程中的内存结构图:

 

 

从上可以看到,无论是引用、非引用,这种直接赋值都不会产生新的变量。

只是当是引用时,is_ref设置为1。当非引用时,is_ref设置为0。

读写复制,就是根据is_ref来进行变量分离的。

 

 

 

当is_ref=1时,是引用变量时,执行“引用下的变量分离”

 

 

 

    $a = 1; 

    $b = $a; 

    $c = &$b; 

       $a = 1;

        $b = $a;

$c = &$b;Memory structure diagram during execution:

When is_ref=0, when it is a non-reference variable, perform "variable separation under non-reference"

$a = 1;

$b = &$a;

$c = $b;

        $a = 1;

$b = &$a;

        $c = $b;

Memory structure diagram during execution:

Only when you really need to change the value of a variable,

Looking back at the code (#2), we can see that in fact, no new variables are generated, and the $test_arr variable is always output. Therefore, this is why it is rare to see variables passed by reference in PHP, but there are still no performance problems.


Excerpted from God's blog

www.bkjia.comtruehttp: //www.bkjia.com/PHPjc/478512.htmlTechArticleThere are two assignment methods in PHP syntax: reference assignment and non-reference assignment. ?php $a = 1; $b = $a; // Non-reference assignment $c = $b; // Reference assignment ?php $a = 1; $b = $a; // Non-reference assignment $c.. .
Statement:
The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn