php的垃圾回收机制对于PHPer来说并不陌生但也不是很熟悉。那么PHP是如何实现无用内存的回收的呢?php变量的内部存储结构首先需要了解一下基础知识,方便理解垃圾回收的原理。大家都知道php是用C写的,所以php变量的内部存储结构也会和C语言有关,也就是zval的结构:struct_zval_struct{union{longlval;双dval;结构{字符*val;国际长度;海峡;哈希表*ht;zend_object_value对象;}价值;//变量值valuezend_uintrefcount__gc;//引用计数内存使用次数,如果为0,删除变量zend_uchar类型;//变量类型zend_ucharis_ref__gc;//区分是否是引用变量};从上面结构体的内容可以看出,每个PHP变量都会由四部分组成:变量类型、值、引用计数以及是否是引用变量注:上面的zval结构体是PHP5.3版本以后的结构体,之前php5.3没有引入新的垃圾回收机制,也就是GC,所以名字里没有_gc;php7版本后,由于性能问题,重写了zval结构,这里不再表述引用计数原理。内部存储结构之后,再了解一下php变量赋值的相关原理和早期的垃圾回收机制变量容器不是数组和对象变量每次给一个常量赋值一个变量,就会生成一个变量容器例子:$a=《徐峥科技成长之路》;xdebug_debug_zval('a')result:a:(refcount=1,is_ref=0)='徐峥的技术成长之路'数组和对象变量会生成一个元素个数+1的变量容器例子:$b=['name'=>'徐峥的技术成长之路','number'=>3];xdebug_debug_zval('b')result:b:(refcount=1,is_ref=0)=array('name'=>(refcount=1,is_ref=0)='徐峥科技成长之路','number'=>(refcount=1,is_ref=0)=3)赋值原理(copy-on-write技术)了解了常量赋值,我们再从内存的角度思考一下变量之间的赋值例子:$a=['name'=>'徐峥的技术成长之路','number'=>3];//创建变量容器,变量a指向变量容器,a的ref_count为1$b=$a;//变量b也指向变量a指向的变量容器,a和b的ref_count为2xdebug_debug_zval('a','b');$b['name']='徐峥的技术成长之路1';//变量b的其中一个元素发生变化,此时会Copy一个新的变量容器,变量b再次指向新的变量容器,a和b的ref_count变为1xdebug_debug_zval('a','b');结果:a:(refcount=2,is_ref=0)=array('name'=>(refcount=1,is_ref=0)='徐峥科技成长之路','number'=>(refcount=1,is_ref=0)=3)b:(refcount=2,is_ref=0)=array('name'=>(refcount=1,is_ref=0)='徐峥的技术成长路径','number'=>(refcount=1,is_ref=0)=3)a:(refcount=1,is_ref=0)=array('name'=>(refcount=1,is_ref=0)='徐峥的技术成长路径','number'=>(refcount=1,is_ref=0)=3)b:(refcount=1,is_ref=0)=array('name'=>(refcount=1,is_ref=0)='徐峥的技术成长之路1','number'=>(refcount=1,is_ref=0)=3)因此,变量a赋值给变量b时,并不会立即生成新的变量容器,而是变量b指向指向的变量容器通过变量a,即内存“Shared”;而当变量b的其中一个元素发生变化时,就会真正发生变量容器拷贝,这就是copy-on-write技术。破坏,实现了内存回收,也是php5.3之前的垃圾回收机制的一个例子:xdebug_debug_zval('a');结果:a:(refcount=2,is_ref=0)='徐峥的技术成长之路'a:(refcount=1,is_ref=0)='徐峥的科技成长之路'循环引用导致的内存泄露但是有一个php5.3之前的垃圾回收机制存在漏洞,即当数组或对象中的子元素引用其父元素时,如果此时删除父元素,这个变量容器将不会被删除,因为它的子元素仍然指向变量容器,但是由于在任何范围内都没有指向变量容器的符号,因此无法清除它,因此会发生内存泄漏,直到脚本执行结束。例如:$a=array('one');$a[]=&$a;xdebug_debug_zval('a');由于本例输出效果不好,所以用图片表示,如图:例:unset($a);xdebug_debug_zval('a');如图:新的垃圾回收机制在php5.3之后引入了rootbuffer机制,即php启动时默认设置了指定zvals个数的rootbuffer(默认为10000),当php发现即存在一个循环引用zval,它会被放入根缓冲区。当rootbuffer达到配置文件中指定的数量(默认为10000)时,将进行垃圾回收,解决循环引用带来的内存泄漏问题。确认为GuidelinesforGarbage1.如果引用计数减为0,变量容器会被清空(free),不属于垃圾2.如果一个zval的引用计数减后仍然大于0,就会进入垃圾循环。其次,在一个垃圾循环中,通过检查引用计数是否减1,检查哪些变量容器的引用计数为零,从而找出哪一部分是垃圾。总结一下垃圾回收机制:1.基于php的引用计数机制(此机制在php5.3之前才有)2.同时使用rootbuffer机制。当php发现有循环引用的zval时,会把它放到Rootbuffer中,当rootbuffer达到配置文件中指定的个数时,会进行垃圾回收,解决循环引用引起的内存泄漏问题(php5.3开始介绍这个机制)
