上次跟你们讲了垃圾回收机制后,有些小伙伴对底层原理比较感兴趣,私信问我了一些关于变量的相关知识,既然你们对变量比较感兴趣,那么此次咱们来系统的讲一下变量的底层原理php
首先,咱们仍是先摆上咱们的zval结构体,即php全部变量都会以zval结构体的形式实现数据库
struct _zval_struct { union { long lval; double dval; struct { char *val; int len; } str; HashTable *ht; zend_object_value obj; } value; //变量value值 zend_uint refcount__gc; //引用计数内存中使用次数,为0删除该变量 zend_uchar type; //变量类型 zend_uchar is_ref__gc; //区分是不是引用变量,是引用为1,不然为0 };
从上面结构体内容能够看出每个php变量都会由变量类型
、value值
、引用计数次数
和是不是引用变量
四部分组成 segmentfault
注:上面zval结构体是php5.3版本以后,php7版本以前的结构数组
看到这里,可能会有小伙伴们问我,php不是有8种数据类型吗?可是为何对应的zvalue的value值只有5种?php7
缘由是这样的,php出于对内存节省的考虑,因此对于一些变量类型作了复用,并无一一对应去定义每一个变量类型函数
下面咱们看一下zvalue的每一个value值所对应的变量类型ui
zval.value.lval => 整型、布尔型、资源 zval.value.dval => 浮点型 zval.value.str => 字符串 zval.value.*ht => 数组 zval.value.obj => 对象
看到这里你们可能会比较奇怪,布尔型和资源是怎么对应到zval.value的lval上的呢?还有,NULL呢?指针
就像咱们会将true和false映射成0和1进行数据库存储同样,php也是这么作的。因此php发现zval的type值是布尔型时,会将布尔型转成0或1存储在zval.value的lval中code
资源对于php来讲属于一个比较特殊的变量,而php会将每一个资源对应的资源标识存储在zval.value的lval中。常见的资源有:文件句柄、数据库句柄等对象
对于NULL来讲,就更好理解了,由于自己经过zval的type值便可区分,因此并无将NULL值存储在zval的value中
php做为一门动态语言,没有先声明变量后赋值的习惯,因此都是拿来一个变量直接就进行了赋值,那么是如何实现的呢?
举例:
$name = "许铮的技术成长之路";
其实每次变量被常量赋值时,都会对应生成一个变量容器。刚才的例子会生成一个变量容器,容器的type是字符串类型,而value值则是许铮的技术成长之路
,且此时该变量容器的ref_count会加1
而变量name
是如何与变量容器关联起来的呢?其实也是使用了php的一个内部机制,即哈希表
。每一个变量的变量名和指向zval结构的指针被存储在哈希表
内,以此实现了变量名到变量容器的映射
上面咱们提到了变量名和变量容器映射的概念。对于php来讲,变量有全局变量和局部变量之分;那么,他们都是存储到一个哈希表
内了么?
其实不是的,变量存储也有做用域的概念。全局变量被存储到了全局符号表
内,而局部变量也就是指函数或对象内的变量,则被存储到了活动符号表
内(每一个函数或对象都单独维护了本身的活动符号表。活动符号表的生命周期,从函数或对象被调用时开始,到调用完成时结束)
变量销毁,分为如下几种状况:
一、手动销毁
二、垃圾回收机制销毁(引用计数清0销毁和根缓冲区满后销毁)
咱们此次主要讲一下手动销毁,即unset,每次销毁时都会将符号表内的变量名和对应的zval结构进行销毁,并将对应的内存归还到php所维护的内存池内(按内存大小划分到对应内存列表中)
而对于垃圾回收机制的销毁,若是你不了解其相关原理,那么我建议你看下我以前写的文章php底层原理之垃圾回收机制
今天,咱们从底层的角度,将变量从生成到销毁讲了一遍。对于变量的生成,咱们是拿常量赋值做为示例讲解的,那么变量之间的赋值呢?是什么原理呢?且听下回分解~