PHP 垃圾回收机制详解

PHP 垃圾回收机制详解

大家好,又见面了,我是全栈君。

点击上方“ 码农编程进阶笔记 ”,选择“置顶或者星标”
文末有干货,每天定时与您相约!
前言:之前对PHP的GC只是了解了个大概,这次详细了解下PHP的垃圾回收机制(GC)。
    介于网上大部分都是PHP5.X的GC,虽然 php5 到 php7 GC部分做出的改动较小,但我觉得还是一起写下来比较好

一、原理

php5和php7的垃圾回收机制都是利用引用计数

 

二、php5和php7不同点

1、PHP5标量数据类型会计数,PHP7标量数据类型不再计数,不需要单独分配内存
2、PHP7的zval 需要的内存不再是单独从堆上分配,不再自己存储引用计数。
3、PHP7的复杂数据类型(比如数组和对象)的引用计数由其自身来存储。

 

三、变量在zval的变量容器中结构

PHP 垃圾回收机制详解

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');

结果如下:可以看到标量(布尔,字符串,整形,浮点型)不再计数了

PHP 垃圾回收机制详解

六、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),
int2

PHP 垃圾回收机制详解

 

七、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开始的

PHP 垃圾回收机制详解

八、循环引用问题

  1、PHP7.1效果

<?php


$a = array('life');
xdebug_debug_zval( 'a' );
echo PHP_EOL;
$a[] = &$a;
xdebug_debug_zval('a');

PHP 垃圾回收机制详解

可以看到,箭头方向表示的就是递归循环引用了

  2、再看看5.3的效果

   PHP 垃圾回收机制详解

说明:在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、可以总将内存泄露保持在一个阈值以下。

PHP 垃圾回收机制详解

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/111810.html原文链接:https://javaforall.net

(0)
全栈程序员-站长的头像全栈程序员-站长


相关推荐

  • Java多维数组声明格式

    Java多维数组声明格式刷题目再次遇到了声明这种最基本形式的考题,以此记录,共勉。关于多维数组声明的形式你知道哪几种呢?首先先上一个错误的例子:这里arr9-arr11在等式右边都错误的在中括号中加了数组大小,导致报错;而arr11、arr12则是等式右边二维数组初始化时没有赋予初始大小导致报错。正确的声明形式如下:本质上arr0、arr4和arr3、arr8是一样的;而arr1-2和arr5-…

    2022年5月5日
    40
  • zookeeper锁原理(Redis分布式锁)

    产生问题例:在分布式(集群)环境下,每台JVM不能实现同步,比如将一个项目部署到多台tomcat服务器,那么用多台JVM在使用时间戳生成唯一的订单号时,会出现订单号重复问题。解决办法:分布式情况下,怎么解决订单号生成不重复:1.使用分布式锁2.提前生成好,订单号,存放在redis。获取订单号时,直接从redis中取。实现分布式锁的方式…

    2022年4月14日
    46
  • Java连接sqlserver(Java项目sqlserver)

    java相对于其他语言(例如c,c++等)连接数据库要方便得多,那么,如何连接呢?1.java与SQLserver连接具体代码如下:importjava.sql.Connection;importjava.sql.DriverManager;importjava.sql.ResultSet;importjava.sql.SQLException;importjava.sql…

    2022年4月12日
    43
  • google app(appstore安卓版下载)

    MiXCodecsapp是一款mix插件包,MiXCodecsapp整个在线的操作非常的简单,是没有任何的广告和收费问题的,MiXCodecsapp适合的类型比较多。软件介绍MiXCodecsapp是一个平滑的、功能齐全的文件管理应用程序MiXplorer文件管理器的插件工具,MiXCodecsapp让你有具有漂亮的界面,它支持批量移动和复制,以及各种分类,水平模式下无限制的标…

    2022年4月12日
    47
  • idea注释颜色修改_福昕阅读器注释颜色

    idea注释颜色修改_福昕阅读器注释颜色修改IDEA注释颜色

    2022年9月27日
    1
  • poetry和poet_poetry和the poetry区别

    poetry和poet_poetry和the poetry区别Poetry的基本使用准备工作如果你是在一个已有的项目里使用Poetry,你只需要执行poetryinit命令来创建一个pyproject.toml文件:poetryinit可看到

    2022年7月31日
    8

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号