根据这个表格可以发现两个有意思的地方:首先是
PHP 的数组其实就是一个
HashTable,这就解释了为什么 PHP 能够支持关联数组了;其次,Resource 就是一
个
long 值,它里面存放的通常是个指针、一个内部数组的 index 或者其它什么只有创
建者自己才知道的东西,可以将其视作一个
handle。
(1)
写复制
(Copy on Write)
<?php
$var = "laruence";
$var_dup = $var;
$var = 1;
?>
PHP
在修改一个变量以前,会首先查看这个变量的
refcount
,如果
refcount
大于
1
,
PHP
就会执行一个分离的例程,
对于上面的代码,当执行到第三行的时候,
PHP
发现
$var
指向的
zval
的
refcount
大于
1
,那么
PHP
就会复制一个新的
zval
出来,将原
zval
的
refcount
减
1
,并修改
symbol_table
,使得
$var
和
$var_dup
分离
(Separation)
。这个机制就
是所谓的
copy on write(
写时复制
)
。
题外话:写时复制技术的一个比较有名的应用是在
unix 类操作系统内核中,当一个进程调
用
fork 函数生成一个子进程的时候,父子进程拥有相同的地址空间内容,在老版本的系统中,子
进程是在
fork 的时候就将父进程的地址空间中的内容都拷贝一份,对于规模较大的程序这个过程
可能会有着很大的开销,更崩溃的是,很多进程在
fork 之后,直接在子进程中调用 exec 执行另
外一个程序,这样原来花了大量时间从父进程复制的地址空间都还没来得及碰一下就被新的进程
地址空间代替,这显然是对资源的极大浪费,所以在后来的系统中,就使用了写时复制技术 ,
fork 之后,子进程的地址空间还是简单的指向父进程的地址空间,只有当子进程需要写地址空间
中的内容的时候,才会单独分离一份(一般以内存页为单位)给子进程,这样就算子进程马上调
用
exec 函数也没关系,因为根本就不需要从父进程的地址空间中拷贝内容,这样节约了内存同
时又提高了速度。
(2)
写改变
(change on writ )
开始在
zval
里面我们看到一个字段
is_ref__gc
,到底如何是怎样产生作用的呢?
现在我们知道,当使用变量复制的时候
,
PHP
内部并不是真正的复制,而是采用指向相同
的结构来尽量节约开销。那么,对于
PHP
中的引用,那又是如何实现呢?
<?php
$var = "laruence";
$var_ref = &$var;
$var_ref = 1;
?>
这段代码结束以后,
$var
也会被间接的修改为
1
,这个过程称作
(change on write:
写时
改变
)
。那么
ZE
是怎么知道,这次的复制是不需要
Separation
的呢?
这个时候就要用到
zval
中的
is_ref
字段了:
对于上面的代码,当第二行执行以后,
$var
所代表的
zval
的
refcount
变为
2
,并且同时置
is_ref
为
1
。
当使用引用时,
php 会把 is_ref__gc 即程序会如下判断该引用是否真实引用,PHP 先
检查
var_ref 代表的 zval 的 is_ref__gc 字段,如果为 1,则不分离,大体逻辑示意如下:
<?php
if((*val)->is_ref || (*val)->refcount<2){