PHP进行内存管理的核心算法一共两项:一是引用计数,二是写时拷贝
声明一个PHP变量的时候,C措辞就在底层天生一个叫做zval的struct(构造体),如下:
zval {string "a" //变量的名字是avalue zend_value //变量的值,联合体type string //变量是字符串类型}
zval struct构造体:
(1)保存php $a的变量名

(2)php $a的变量类型
(3)php变量$a的zend_value联合体
如果给变量赋值了,比如“hello world”,那么C措辞就在底层再天生一个叫做zend_value的union(联合体)
zend_value {string "hello world" //值的内容refcount 1 //引用计数}
zend_value的union(联合体)
(1)保存php $a的变量的值hello world
(2)记录php $a变量引用次数
看到 zval struct构造体和zend_value,如果口试官问你php变量为什么能够保存字符串"123"也能保存数字123,你知道该怎么回答了吧?就答出重点zval中有该变量的类型,当是字符串123的时候,type便是string,此时value指向“123”;当是整数123的时候,zval的type为int,value为123。
作甚引用计数?代码实战解析php变量引用计数
$a = 'hello,world';echo xdebug_debug_zval( 'a');//refcount=1$b = $a;echo xdebug_debug_zval( 'a'); //$b引用$a,故变量a,refcount=2$c = $a;echo xdebug_debug_zval( 'a'); //$c引用$a,故变量a,refcount=3unset( $c );echo xdebug_debug_zval( 'a');//删除了$c的引用,故变量a,refcount=2
运行结果:
a:(refcount=1, is_ref=0)string 'hello,world' (length=11)
a:(refcount=2, is_ref=0)string 'hello,world' (length=11)
a:(refcount=3, is_ref=0)string 'hello,world' (length=11)
a:(refcount=2, is_ref=0)string 'hello,world' (length=11)
作甚拷贝复制?
$a = 'hello';$b = $a;//$a赋值给$b的时候,$a的值并没有真的复制了一份echo xdebug_debug_zval( 'a');//$a的引用计数为2$a = 'world';//当我们修正$a的值为123的时候,这个时候就不得已进行复制,避免$b的值和$a的一样echo xdebug_debug_zval( 'a');///$a的引用计数为1
运行结果:
a:(refcount=2, is_ref=0)string 'hello' (length=5)
a:(refcount=1, is_ref=0)string 'world' (length=5)
实在,当你把$a赋值给$b的时候,$a的值并没有真的复制了一份,这样是对内存的极度不尊重,也是对韶光繁芜度的极度不尊重,打算机仅仅是将$b指向了$a的值而已,这就叫多快好省。那么,什么时候真正的发生复制呢?便是当我们修正$a的值为123的时候,这个时候就不得已进行复制,避免$b的值和$a的一样。
通过大略的案例阐明清楚了两个要点:引用计数和写时拷贝。
垃圾回收机制:当一个zval在被unset的时候、或者从一个函数中运行完毕出来(便是局部变量)的时候等等很多地方,都会产生zval与zend_value发生断开的行为,这个时候zend引擎须要检测的便是zend_value的refcount是否为0,如果为0,则直接KO free空出内容来。如果zend_value的recount不为0,这个value不能被开释,但是也不代表这个zend_value是明净的,由于此zend_value依然可能是个垃圾。
(1)当php变量$a的refcount=0时,变量$a就会被垃圾回收
(2)当php变量$a的refcount>0时,变量$a也可能被认为是垃圾
什么样的情形会导致zend_value的refcount不为0,但是这个zend_value却是个垃圾呢?
$arr = [ 1 ];$arr[] = &$arr;unset( $arr );
这种情形下,zend_value不会能开释,但也不能放过它,不然一定会产生内存泄露,以是这会儿zend_value会被扔到一个叫做垃圾回收堆中,然后zend引擎会依次对垃圾回收堆中的这些zend_value进行二次检测,检测是不是由于上述两种情形造成的refcount为1但是自身却确实没有人再用了,如果一旦确定是上述两种情形造成的,那么就会将zend_value彻底抹掉开释内存。
垃圾回收发生在什么时候?有些同学可能有疑问,便是php不是运行一次就销毁了吗,我要gc有何用?并不是的,首先当一次fpm运行完毕后,末了一定还有gc的,这个销毁便是gc;其次是,内存都是即用即开释的,而不是攒着非得到末了,你想想一个范例的场景,你的掌握器里的某个方法里用了一个函数,函数须要一个巨大的数组参数,然后函数还须要修正这个巨大的数组参数,你们该当是函数的运行范围里面修正这个数组,以是此时会发生写时拷贝了,当函数运行完毕后,就得赶紧开释掉这块儿内存以供给其他进程利用,而不是非得等到本地fpm request彻底完成后才销毁。
(1)fpm运行完毕后,末了一定会gc的
(2)运行过程中,也会gc的,内存都是即用即开释的,而不是攒着非得到末了gc
php7进阶到架构师干系阅读php7进阶到架构师干系阅读
https://www.kancloud.cn/gofor/gofor