PHP 垃圾回收机制详解
2024-10-10 20:44:29
前言:之前对PHP的GC只是了解了个大概,这次详细了解下PHP的垃圾回收机制(GC)。
介于网上大部分都是PHP5.X的GC,虽然 php5 到 php7 GC部分做出的改动较小,但我觉得还是一起写下来比较好
一、原理
php5和php7的垃圾回收机制都是利用引用计数
二、php5和php7不同点
1、PHP5标量数据类型会计数,PHP7标量数据类型不再计数,不需要单独分配内存
2、PHP7的zval 需要的内存不再是单独从堆上分配,不再自己存储引用计数。
3、PHP7的复杂数据类型(比如数组和对象)的引用计数由其自身来存储。
三、变量在zval的变量容器中结构
zval中,除了存储变量的类型和值之外,还有is_ref字段和refcount字段
1、is_ref:是个bool值,用来区分变量是否属于引用集合。
2、refcount:计数器,表示指向这个zval变量容器的变量个数。
四、PHP5.3标量在zval容器例子
注意:php5.3中将一个变量 = 赋值给另一个变量时,不会立即为新变量分配内存空间,而是在原变量的zval中给refcount加1。 只有当原变量或者发生改变时,才会为新变量分配内存空间,同时原变量的refcount减 1 。当然,如果unset原变量,新变量直接就使用原变量的zval而不是重新分配。&引用赋值时,原变量的is_ref 加1. 如果给一个变量&赋值,之前 = 赋值的变量会分配空间。
<?php
$a = 1;
xdebug_debug_zval('a');
echo PHP_EOL;
$b = $a;
xdebug_debug_zval('a');
echo PHP_EOL; $c = &$a;
xdebug_debug_zval('a');
echo PHP_EOL; xdebug_debug_zval('b');
echo PHP_EOL; 结果如下:
a:(refcount=1, is_ref=0),int 1
a:(refcount=2, is_ref=0),int 1
a:(refcount=2, is_ref=1),int 1
b:(refcount=1, is_ref=0),int 1
五、PHP7.X 标量在zval容器例子
<?php $a = 1;
xdebug_debug_zval('a');
echo PHP_EOL;
$b = $a;
xdebug_debug_zval('a'); 结果如下:可以看到标量(布尔,字符串,整形,浮点型)不再计数了
六、PHP5.3复合类型数组和对象在zval容器例子
<?php
$a = array( 'meaning' => 'life', 'number' => 42 );
xdebug_debug_zval( 'a' );
echo PHP_EOL;
class Test{
public $a = 1;
public $b = 2; function handle(){
echo 'hehe';
}
} $test = new Test();
xdebug_debug_zval('test'); 结果如下:可以看出,数组用了比数组长度多1个zval存储。数组分配了三个zval容器:a meaning number
a:(refcount=1, is_ref=0),
array
'meaning' => (refcount=1, is_ref=0),
string
'life' (length=4)
'number' => (refcount=1, is_ref=0),
int
42
test:(refcount=1, is_ref=0),
object(Test)[1]
public 'a' => (refcount=2, is_ref=0),
int
1
public 'b' => (refcount=2, is_ref=0),
int
2
七、PHP7.X复合类型数组和对象在zval容器例子
<?php $a = array( 'meaning' => 'life', 'number' => 42 );
xdebug_debug_zval( 'a' );
echo PHP_EOL;
class Test{
public $a = 1;
public $b = 2; function handle(){
echo 'hehe';
}
} $test = new Test();
xdebug_debug_zval('test'); 结果如下:可以明显的看到数组a的refcount=2,后经测试发现数组的refcount都是从2开始的
八、循环引用问题
1、PHP7.1效果
<?php $a = array('life');
xdebug_debug_zval( 'a' );
echo PHP_EOL;
$a[] = &$a;
xdebug_debug_zval('a');
可以看到,箭头方向表示的就是递归循环引用了
2、再看看5.3的效果
说明:在5.2及更早版本的PHP中,没有专门的垃圾回收器GC(Garbage Collection),引擎在判断一个变量空间是否能够被释放的时候是依据这个变量的zval的refcount的值,
如果refcount为0,那么变量的空间可以被释放,否则就不释放,这是一种非常简单的GC实现。现在unset ($a),那么array的refcount减1变为1.现在无任何变量指向这个zval,
而且这个zval的计数器为1,不会回收。 结果:尽管不再有某个作用域中的任何符号指向这个结构(就是变量容器),由于子元素“1”仍然指向数组本身,所以这个容器不能被清除 。
因为没有另外的符号指向它,用户没有办法清除这个结构,结果就会导致内存泄漏。 在php5.3的GC中,针对的垃圾做了如下说明:
1:如果一个zval的refcount增加,那么此zval还在使用,肯定不是垃圾,不会进入缓冲区
2:如果一个zval的refcount减少到0, 那么zval会被立即释放掉,不属于GC要处理的垃圾对象,不会进入缓冲区。
3:如果一个zval的refcount减少之后大于0,那么此zval还不能被释放,此zval可能成为一个垃圾,将其放入缓冲区。PHP5.3中的GC针对的就是这种zval进行的处理。
开启/关闭:垃圾回收机制可以通过修改php配置实现,也可以在程序中使用gc_enable() 和 gc_disable()开启和关闭。
九、垃圾回收算法
1、对每个根缓冲区中的根zval按照深度优先遍历算法遍历所有能遍历到的zval,并将每个zval的refcount减1,同时为了避免对同一zval多次减1(因为可能不同的根能遍历到同一个zval),
每次对某个zval减1后就对其标记为“已减”。 2、再次对每个缓冲区中的根zval深度优先遍历,如果某个zval的refcount不为0,则对其加1,否则保持其为0。 3、清空根缓冲区中的所有根(注意是把这些zval从缓冲区中清除而不是销毁它们),然后销毁所有refcount为0的zval,并收回其内存。 如果不能完全理解也没有关系,只需记住PHP5.3的垃圾回收算法有以下几点特性: 1、并不是每次refcount减少时都进入回收周期,只有根缓冲区满额后在开始垃圾回收。 2、可以解决循环引用问题。 3、可以总将内存泄露保持在一个阈值以下。
以上就是全部内容了
最新文章
- HTML5-03 页面布局
- iOS开发进阶
- C#加密解密(DES,AES,Base64,md5,SHA256,RSA,RC4)
- 免费VPN 实测可用
- NGUI所见即所得之UIAtlasMaker , UIAtlas (2)
- Tomcat中配置多个端口
- 第五章 Spring3.0 、Hibernate3.3与Struts2的整合
- SVN操作手册(part1&;part2)——SVN安装
- java发送post请求 ,请求数据放到body里
- python编码问题和逻辑运算
- Convert.ToInt32、(int)和int.Parse三者的区别
- Bash shell中的位置参数$#,$*,$@,$0,$1,$2...及特殊参数$?,$-等的含义
- logback日志丢失的情况之一
- maven 安装本地jar
- npm Error: Cannot find module &#39;./auth.js&#39;
- Tomcat post参数长处理
- WINDOWS SERVER 2016 设置使用照片查看器查看图片
- maven的包冲突
- 深入出不来nodejs源码-内置模块引入再探
- Gruntfile.js文件配置项
热门文章
- 获得用户的真实ip HTTP_X_FORWARDED_FOR
- 感知哈希算法——Python实现【转】
- Python 简易的异步协程使用方法
- 基于Anaconda编译caffe+pycaffe+matcaffe in Ubuntu[不用sudo权限]
- 201871010124-王生涛 《面向对象程序设计(java)》第八周学习总结
- day29 8_8 TCP上传文件socketserver的应用
- vmware centos 桥接模式 联网记录
- 【java】@SuppressWarnings
- Codeforces.1029D.Isolation(DP 分块)
- Linux性能优化实战学习笔记:第三十七讲