做者:王澍php
PHP中zval是全部变量的基础。(zend_type.h 121行)ubuntu
其中zend_value存储了具体数据,结构如图: (zend_type.h 101行)api
整个zval结构体,占用16字节,就支持了php全部类型。数组
PHP7中用如此简单而巧妙的zval存储了全部类型数据,那么一个不肯定长度的字符串又如何能存储在一个16字节的zval中呢?数据结构
<?php $a = "hello world"; echo $a;
经过GDB调试能够看到:性能
type = 6,对照类型的定义,能够看到类型是IS_STRING (zend_type.h 303行)编码
因为咱们的字符串长度不必定,因此单靠zval的16个字节是没法直接存储的,因而经过value中的str指向真正存储字符串的内存地址。经过打印咱们能够看到,这个地址的类型是zend_stringspa
先看一下它的数据结构,如图 (zend_type.h 169行)3d
zend_string结构体中的gc
头部先是gc,能够看一下其余复杂类型,头部都有一个gc,它的做用是什么?
看看gc的数据结构,如图:指针
那么做用就比较好猜想了,在程序执行gc或其余操做的时候,对于任意一个复杂类型,指针头部就是gc,里面不光有引用计数,而且能经过u.v.type肯定该复杂类型的真正类型。
zend_string结构体中的h
从名字能够猜想,这是字符串的hash,空间换时间的思想,把计算好的hash保存下来,提升性能。
zend_string中的len
比较明显,它存储了字符串的长度。
zend_string中的val[1]
这种写法是c里面的柔性数组,这里存储了整个字符串,经过这个方式保证字符串所在的内存地址是与该结构体内存地址紧密相连的,减小了去另一个块内存取值的时间。
(ps:留个小疑问,gdb就能够追踪到。柔性数组是否占内存空间?zend_string的对齐后是什么结构?总体占多大?)
了解过结构自己,能够打印一下内容来看看,如图
该地址内存储的确实是hello world,为何gc中的refcount是0呢?
缘由是这样的:
修改一下代码,看一下临时字符串
<?php $a = "hello world".time(); echo $a;
打印一下这个变量的zval,refcount为1,如图
对于临时字符串,应该是每有一个被赋值的变量时,该zend_string中的引用计数+1,而且在引用计数为0时,释放这块内存。
<?php $a = "hello world".time(); echo $a; $b = $a; echo $b;
当为$a赋值完成时,$a,在栈上第一个位置,类型为6,IS_STRING,取value中的str,地址为:**0x7ffff4402c30**,能够看到内容,zend_string引用计数为1。
当为$b赋值完成时,$b在栈上第二个位置,类型为6,IS_STRING,取value中的str一样地址为:**0x7ffff4402c30**,zend_string引用计数为2。
大体引用状况能够画出:
对于变量直接赋值,上面已经画出了引用关系,那么若是是引用类型呢?
<?php $a = "hello world".time(); echo $a; $b = &$a; echo $b;
当为$a赋值完成时,$a,在栈上第一个位置,类型为6,IS_STRING,取value中的str,地址为:**0x7ffff4402c30**,能够看到内容,zend_string引用计数为1。
当$b赋值为引用类型时,$b在栈上第二个位置,类型为10 , IS_REFERENCE,取value中的ref能够看到内容。
这时$a的类型是否发生改变?是否仍是字符串类型?直接打印$a看一下。这时$a的类型变成了10,IS_REFERENCE,打印value中的ref,地址与$b的ref相同!
在$b引用$a的时候,$a与$b都变成引用类型,该引用类型指向了中有一个zval,类型为6,IS_STRING,value中的str指向了一个zend_string,而且zend_string引用计数为1.
大体引用状况如图:
<?php $a = “string”; $b = “double”; echo $a; echo $b;
以咱们上面的结论,$a与$b都属于常量字符串。
打印$a的zend_string,如图
打印$b的zend_string,如图
可见$b符合预期,可是$a颠覆了以上的理论。
那问题出在了哪?
通过GDB的追踪,能够看到a和b都在栈上,而且都是string类型。
可是,其中value中的str地址有很大不同。
首先看变量a
在栈的第一个位置,str的值是 0x11522c0
其次看变量b
在栈上第二个位置,str的值是 0x7ffff4401880
了解PHP的内存分配的话,能够看出b的字符串,分配在了 0x7ffff440000 这个chunk上,属于第一个page页,0x7ffff4401000
而a的字符串很显然不是这个规则,他没有分配在chunk上,而是很特殊的一个地址。
因此string这个字符串,不是_emalloc分配的。
那么采用个笨办法,我把 0x11522c0强转 (zend_string *)0x11522c0 ,而后看里的值何时放进去的。
PHP版本 7.1.0
第一个节点: php_cli.c中的 1345行
sapi_module->startup(sapi_module)
第二个节点: php_cli.c 中的424 行
php_module_startup(sapi_module, NULL, 0)
第三个节点: main.c 中的 2123行
zend_startup(&zuf, NULL);
第四个节点: zend.c 中的768行
zend_interned_strings_init();
很接近了哦
第五个节点: zend_string.c中的103
zend_intern_known_strings(known_strings, (sizeof(known_strings)
在这里打印一下,know_strings,这里能够看到,file,line,function,class,object等等,以及string在这里就初始化了!
对应声明的地址在 zend_string.h 383行
这里尚未初始化字面量,因而这些字符串与字面量的状况有些不同。
一样是字符串在PHP中有不少种不一样状况。