龙空技术网

php变量之写时复制机制(copy on write)

奋发有为风铃jC 96

前言:

此刻看官们对“php变量底层实现”可能比较珍视,你们都需要知道一些“php变量底层实现”的相关文章。那么小编在网摘上汇集了一些对于“php变量底层实现””的相关资讯,希望小伙伴们能喜欢,看官们一起来学习一下吧!

Copy on write,PHP的一个很重要的机制,我们先以最简单的变量来介绍这个机制,在说这个之前,先来介绍下弱类型是怎么实现的,PHP变量在C语言底层中的代码 :

typedef struct _zval_struct zval;typedef unsigned int zend_uint;typedef unsigned char zend_uchar; struct _zval_struct { zvalue_value value; /*注意这里,这个里面存的才是变量的值*/ zend_uint refcount__gc; /*引用计数*/ zend_uchar type; /* 变量当前的数据类型 */ zend_uchar is_ref__gc; /*变量是否引用*/};   typedef union _zvalue_value { long lval; /*PHP中整型的值*/ double dval; /*PHP的浮点数值*/ struct {  char *val; int len; } str; /*PHP的字符串*/ HashTable *ht; /*数组*/ zend_object_value obj; /*对象*/} zvalue_value;

大家可以发现,实际上我们在PHP用的变量,低层是一个结构体zval,里面的zvalue_value结构体实际上是个联合体,这个联合体才是实际存放着PHP的变量值,下面我们以实际的PHP代码例子来表示整个工作过程,注意上面的引用计数。先来看C语言的,首先是非函数部分,函数部分下一章节来讲

int i = 4; //alloca方式在内存中分配空间,这个变量在内存中的栈区int j = i; //alloca方式在内存中分配空间,并且将原先内存空间里面的数据复制到新的内存空间中,这个变量在内存的栈区int j = 5; //不分配内存空间,对变量j所在的栈区空间的数据进行修改来看PHP部分的$i = 4; //内核创建一个zval指针,并且为其以堆的方式开辟空间,让指针指向这个空间,将zval中的成员引用计数置为1,类型标记为整形,并且申请一个zvalue_value指针,同样以堆的方式以其开辟空间,同时将该联合体中的lval赋值为4,并且在symbal_table的hash表中记录变量i和zval指针的映射关系$j = $i; //没有在申请内存空间,在zval的成员中引用计数标记为2$j = 5; //内核重新创建zval指针,重复下上面的步骤,我就不重复说明了,重点是将旧的zval引用计数标记为1

从这个地方发现几个重要点

1.所有的php变量开辟的内存空间都是在堆中,无论是临时变量还是全局变量,只是php的临时变量记录在active_symbal_table表中,全局变量记录中symbal_table表中

2.php干嘛比C慢。多做了这么多事,能不慢吗?

3.当php类似$j = $i这种变量赋值时,是没有内存开销的,也就是你赋值个几万个,也只是引用计数变成几万而己,这个和C语言是不一样的。而当变量的值发生变化时,才会进行重新开辟内存空间,这个机制我们称为写时复制机制

额外细节部分,当php内核发现,int的数值溢出时,也就是超出整型的范围时,自动转换为float,有兴趣的读者可以自己写个很大的整型,但是不能超出float取值范围,看看var_dump数据类型是什么。

最后部分:

php对象部分因为默认是引用方式的,所以就是赋值完,再改变对象的成员变量,也不会启用写时复制的,如以下

class Test { public $var = 999;}$test1 = new Test();$test2 = $test1; //只是引用计数加1而己,没有开辟新的内存空间$test2->var = 1000;echo $test1->var; //此时的值也为1000$test3 = clone $test1; //这个才是正在重新开辟新的内存空间

标签: #php变量底层实现 #引用计数 js