简述 php垃圾回收机制
对于一个PHPer 来说,程序运行时出现内存益出可谓是见多不怪的,从而想到的解决方法无非是以下几点:
1, 设置脚本运行内存大小,
2, 脚本运行时间等,
但, 最终这些都只能解决表面问题甚至更改过配可能还后导致内存益出。 那我们接下来就了解一下为何会导致溢出
1,循环嵌套(死循环, 套娃式循环)
2,恶性递归
3,多个Array或object 引用变量,导致内存泄漏
分析导致上述问题的原因
造成问题1,2略过,自行检查代码问题,至于问题3我们首先了解下php的垃圾回收机制。
php 5.3版本前是用应用计数的方式来进行垃圾回收,建议大家安装php的xdebug扩展,这样可以更清楚的了解。
述语普及
refcount -- **引用计数**的次数
is_ref(booler) --- 布尔类型, php用来区分是否为引用变量的标识
interned --- 当 字符, 数字, booler不进行引用次数占用
简单类型的探讨 (string|int|booler)
e g,1 赋值变量
$a = "how are you ?"
xdebug_debug_zval('a');
结果为
a: (refcount=1, is_ref=0)='how are you ?'
e g,2 引用赋值
$b = &$a
xdebug_debug_zval('a', 'b');
a: (refcount=2, is_ref=1)='how are you ?'
b: (refcount=2, is_ref=1)='how are you ?'
此处: $b引用$a php内部标识为共享内存则引用次数+1, 更改引用变量状态
e g,3 销毁引用变量中的任意一个变量
unset($a)
xdebug_debug_zval('b');
b: (refcount=1, is_ref=1)='how are you ?'
负责类型探讨 (array|object)
e g,4
$a[] = 'aaa'
$a[] = 'bbb'
xdebug_debug_zval('a');
a (refcount=1, is_ref=0)
array (size=2)
0 => (interned, is_ref=0)string 'aaa' (length=3)
1 => (interned, is_ref=0)string 'bbb' (length=3)
unset($a)
表示该数组已销毁
a: no such symbol a:
e g,5 引用自身
$a[] = &$a
xdebug_debug_zval('a');
a (refcount=1, is_ref=0)
array (size=3)
0 => (interned, is_ref=0)string 'aaa' (length=3)
1 => (interned, is_ref=0)string 'bbb' (length=3)
2 => (refcount=2, is_ref=1)null
注:此处不方面展示, 故用图代替
此处,引用自身导致形成闭环从而导致当unset()后, 只是单纯销毁一部分
unset($a)
xdebug_debug_zval('a')
结果为: 剩余这部分将无法回收, 从而导致内存泄漏,要想回收只能等待脚本结束后,php进程|线程结束后将泄漏的内存销毁处理 (php.version < 5.3 )
犹豫上述的图比较抽象, 不易理解可参考如下代码配合上图理解
$initMemory = memory_get_usage();
echo "execute init memory : {$initMermory} <br>";
$a = array('one');
$a = &$a;
unset($a);
$endMemory = memory_get_usage();
echo "execute after memory : {$endMemory} <br>"
//如果在使用unset() 后 $initMemory == $endMemory 表示没有内存泄漏, 如果不等则反之
// 输出结果为
execute init memory:4861800
execute after memory:4862200
接下来了解一下新的垃圾回收机制:
php.version > 5.3 之后引入了梗缓存机制, php启动时默认设置指定zval数量的根缓冲区(default 10000),
当php发现存在循环引用的zval时便其加入根缓存区, 当缓存区达到上限后,就会进行垃圾回收, 以此解决**循环引用**的内存泄漏问题
总结
判定垃圾界限:
1, 引用计数减少为0时 自动清除, 不属于垃圾
2, 如果zval的引用次数减少后还大于0, 那么进入**垃圾回收周期**
垃圾回收周期含义: 当存储zval根缓存满后, 算法执行
模拟删除 : 引用计数减1,导致模拟删除
模拟恢复 : 引用计数真的不为0时, 执行模拟删除的逆运算
真的删除 : 引用计数为0
详细介绍请看 : https://blog.csdn.net/weixin_40341587/article/details/78710081
垃圾回收机制:
1, php.version < 5.3 以引用计数机制进行判断
2, php version > 5.3 后给予引用计数判定法则,将引用计数不为0的垃圾添加到预分配的zval中,当zval到达峰值时, 会进行垃圾回收,从而解决"循环引用"的内存泄漏问题