引用计数
php采用的内存管理和垃圾回收方法是基于引用计数的。之前说过,在zval结构里有一个refcount是表示引用计数,还有一个is_ref表示是否是个引用变量。那么php代码的实际运行中,又是如何处理的呢?
比如这样的php代码:
$a = “hello”;
$b = $a;
这时候并不像很多人认为的那样,在内存里把”hello”这个字符串复制了一份,而只是把$b指向了和$a对应的同一个zval,然后把那个zval的refcount + 1。这样避免了一次内存拷贝。但如果在这之后改变了其中一个变量的值,比如$b.= ” world”;又会如何呢?这时候才会分配一个新的zval给$b,然后把原先那个zval的refcount – 1。这就是传说中的copy on write。就是说,在改变值得时候才会有内存拷贝。
那么引用变量又会如何呢? 比如
$a = “hello”;
$b = &$a;
和前面一样,$a, $b还是指向同一个zval。只是还要把这个zval的is_ref置为1。之后再改变$a或者$b的时候就不会再发生拷贝。那么
$a = “hello”;
$b = &$a;
$c = $a;
这时又会如何呢?因为$c并不是一个引用变量,因此不能和$a, $b共用一个zval。因此在$c = $a的时候会直接产生一个新的zval。
因此,在php中,使用引用对改善性能并不会有多少作用,通常情况下还会使情况更糟。所以,引用还是只在真正需要的时候才用为好。
再说说垃圾回收。每个zval都有一个refcount表示它的变量的引用数。不管对于普通变量还是引用变量都是如此。refcount的初始值一般为1。每当增加一个引用时就+1,减少一个引用,比如unset时就会-1。当refcount为0的时候,php就会把它释放掉。这就是基于引用计数的垃圾回收方法。
使用zval
初始化zval
MAKE_STD_ZVAL(zval*);
这个宏的左右是创建一个zval,完成初始化(如将ref_count置为1,isref置为false)并把指针赋给参数。
赋值
写扩展的时候不可避免的要用到把一个zval复制到另一个zval,就是类似$a = $b;的操作。对于简单的值或许手动维护引用计数之类的还不算很麻烦但对于数组,对象之类的就需要一层层递归进去,因此就有了一个zval_copy_ctor来做着件事情。
原有一个zval* p_zval_b,
zval* p_zval_a;
MAKE_STD_ZVAL(p_zval_a); //初始化p_zval_a
*p_zval_a = *p_zval_b;
zval_copy_ctor(p_zval_a);
这里,zval_copy_ctor完成了类似赋值的操作,包括引用计数处理,对于hash值的成员处理等。
释放一个zval则是使用zval_ptr_dtor(**zval)。注意它的参数。它会释放掉为这个zval所分配的内存。
其实引用在PHP只有一种实际作用….在一个函数需要改变两个参数的值..这时,使用引用,基本想使用指针一样使用它,其他任何情况,皆没有必要…