转载请注明出处http://blog.csdn.net/fanhengguang_php
PHP内核原理 Zvals内存管理
zval结构有两个功能:第一,用于存储一个变量的值以及变量类型。第二,有效的管理内存中的zval变量的值,本章将会介绍这个功能。
接下来我们看一下引用计数和copy-on-write 这两个概念,以及在扩展中如何应用。
值和引用
在php中所有的变量都是值传递,除非你显示的指明引用传递。即任何时刻你传递一个变量给个函数或者给另外一个变量赋值,你得到的两个变量都会拥有一份独立的值的拷贝。看以下例子
<?php
$a = 1;
$b = $a;
$a++;
// Only $a was incremented, $b stays as is:
var_dump($a, $b); // int(2), int(1)
function inc($n) {
$n++;
}
$c = 1;
inc($c);
// The $c value outside the function and the $n inside the function are distinct
var_dump($c); // int(1)
虽然上面例子有些简单,但需要意识到这是php种一个基本的规则, 特别的这个规则页适用于对象。
<?php
$obj = (object) ['value' => 1];
function fnByVal($val) {
$val = 100;
}
function fnByRef(&$ref) {
$ref = 100;
}
// The by-value function does not modify $obj, the by-reference function does:
fnByVal($obj);
var_dump($obj); // stdClass(value => 1)
fnByRef($obj);
var_dump($obj); // int(100)
人们经常说自从php5对象对象是自动的按引用传递的, 但是通过以上例子看出这是错的:按值传递的函数不能修改传递给他的变量,而按引用传递的函数可以。
看一下例子:
<?php
class myclass {
public $prop;
}
function myfun($obj) {
$obj->prop = 'world';
}
$obj = new myclass();
$obj->prop = 'hello';
var_dump($obj);
myfun($obj);
var_dump($obj);
打印出:
object(myclass)#1 (1) {
["prop"]=>
string(5) "hello"
}
object(myclass)#1 (1) {
["prop"]=>
string(5) "world"
}
对象确实表现出引用传递的行为:虽然你不能将其赋值为一个完全不同的值, 但是你可以在函数中修改对象的成员。这是由于对象的值仅仅是一个用来查找实际内容的ID, 引用传递可以阻止你将其ID改为一个不同的对象或者不同的类型,但是并不能阻止你修改对象的实际的值。
以上也适用于resource类型。因为它也是同样仅仅存储了用于查找实际值的ID,所以同样的按引用传递可以阻止你修改其resource ID或者不同的类型,但是并不能阻止你resource的内容(如修改文件的指针位置)。
引用计数和写时复制
稍加思考你会得到这样的结论:php一定是做了可怕的大量拷贝。每次给函数传递变量,其值都会被拷贝一次,对于整形int或者double类型这可能没啥问题,但是想像一下给函数传递一个拥有百万元素的数组,每次调用都拷贝百万的元素将是多么低效。
为了避免拷贝,php使用了写时复制的方法:一个zval可以被多个变量、函数等共享,只要他们对变量是只读的不会修改她。如果一个变量想要做修改,在修改之前需要将zval拷贝一份。
如果一个zval可以被共享,那么php需要一个方法判断何时这个zval不被使用了,不再使用的zval将会被释放掉。PHP通过简单的跟踪一个zval被引用次数来解决这个问题, 注意这里引用指的是一个zval被变量、函数等使用,而不是变量引用(如&方式引用变量)。引用个数存储在zval结构的refcount__gc
成员变量中。
为了理解引用计数原理,考虑下面的例子:
<?php
$a = 1; // $a = zval_1(value=1, refcount=1)
$b = $a; // $a = $b = zval_1(value=1, refcount=2)
$c = $b; // $a = $b = $c = zval_1(value=1, refcount=3)
$a++; // $b = $c = zval_1(value=1, refcount=2)
//