首页 > 编程语言 >PHP GC回收机制详解

PHP GC回收机制详解

时间:2024-01-25 14:24:29浏览次数:27  
标签:refcount destruct num GC new gc PHP ref 详解

前言

GC的全称是Garbage Collection也就是垃圾回收的意思,在PHP中,是使用引用计数和回收周期来自动管理内存对象的,当一个对象被设置为NULL,或者没有任何指针指向时,他就会变成垃圾,被GC机制回收掉。

环境配置

php.ini终配置好xdebug,xdebug_debug_zval是用来查看容器变量内容的函数

<?php 
$a = "F12";
xdebug_debug_zval("a");
?>


在PHP GC机制中,当程序终止时就会让变量的refcount减1,如果refcount-1为0的话,就会销毁回收该变量

引用计数

is_ref表示该变量是否被引用,操作系统学的好的同学应该很容易理解该内容

<?php 
  $a = "F12";
  $b = &$a;
  xdebug_debug_zval("a");
?>

# 运行结果
a: (refcount=2, is_ref=1)='F12'

$b是$a的引用,所以is_ref=1,同时refcount也会加1,因为此时是有两个变量的(两变量指向同一个地址),所以销毁时要让refcount减2。
当变量是array类型时,也是一样的规则

<?php 
  $a = "F12";
  $arr = array(0=>"test", 1=>&$a);
  xdebug_debug_zval("arr");
?>
# 运行结果
arr: (refcount=1, is_ref=0)=array (0 => (refcount=1, is_ref=0)='test', 1 => (refcount=2, is_ref=1)='F12')

如果我们在引用前将$a销毁会发生什么?

<?php 
  $a = "F12";
  unset($a);
  $arr = array(0=>"test", 1=>&$a);
  xdebug_debug_zval("a");
  xdebug_debug_zval("arr");
?>
# 运行结果
a: (refcount=2, is_ref=1)=NULL
arr: (refcount=1, is_ref=0)=array (0 => (refcount=1, is_ref=0)='test', 1 => (refcount=2, is_ref=1)=NULL)
<?php 
  $a = "F12";
  $arr = array(0=>"test", 1=>&$a);
  unset($a);
  xdebug_debug_zval("a");
  xdebug_debug_zval("arr");
?>
# 运行结果
a: no such symbol
arr: (refcount=1, is_ref=0)=array (0 => (refcount=1, is_ref=0)='test', 1 => (refcount=1, is_ref=1)='F12')

第一种情况,$a没有被销毁,因为在之后又引用了$a,所以$a只是指向了一个NULL,第二种情况就把$a销毁了

PHP GC在反序列化中的使用

一个简单的demo

<?php
class gc{
    public $num;
    public function __construct($num)
    {
        $this->num=$num;
        echo "construct(".$num.")"."\n";
    }
    public function __destruct()
    {
        echo "destruct(".$this->num.")"."\n";
    }
}
$a=new gc(1);
$b=new gc(2);
$c=new gc(3);

# 运行结果
construct(1)
construct(2)
construct(3)
destruct(3)
destruct(2)
destruct(1)

先创建的对象最后销毁,看看变量的内容情况:

可以看到refcount为1,所以当程序结束时,减1就会被回收
如果我们不把new的gc对象赋值给$a会怎样?

<?php
class gc{
    public $num;
    public function __construct($num)
    {
        $this->num=$num;
        echo "construct(".$num.")"."\n";
    }
    public function __destruct()
    {
        echo "destruct(".$this->num.")"."\n";
    }
}
new gc(1);
$b=new gc(2);
$c=new gc(3);

# 运行结果
construct(1)
destruct(1)
construct(2)
construct(3)
destruct(3)
destruct(2)

可以看到第一个gc对象,创建完就被回收了,因为没被其它变量引用,它的refcount一开始就是0,所以直接被回收

绕过Exception异常

思路一

一个简单的demo:

<?php
class gc{
    public $num;
    public function __construct($num)
    {
        $this->num=$num;
    }
    public function __destruct()
    {
        echo "Hello World!";
    }
}
$a = new gc(1);
$ser = serialize($a);
$b = unserialize($ser);
throw new Exception("F12 is bad");

正常来说会输出一个Hello World!,但是因为触发了异常,所以对象并没有被回收

我们修改一下代码:

<?php
class gc{
    public $num;
    public function __construct($num)
    {
        $this->num=$num;
    }
    public function __destruct()
    {
        echo "Hello World!";
    }
}
$a = array(0=>new gc(1),1=>1);
$ser = serialize($a);
echo $ser;
$ser = 'a:2:{i:0;O:2:"gc":1:{s:3:"num";i:1;}i:0;i:1;}';
$b = unserialize($ser);
throw new Exception("F12 is bad");

这里我们我们修改序列化的内容,将$a[0]随便指向谁,从而使new的gc对象没有引用的变量,所以触发提前回收,跟上面举的直接new gc,并不赋值是一个道理

思路二

这种方法更加简单粗暴,我们只需要让序列化的数据出错,那么当反序列化时出错时,也会让该对象提前回收

<?php
class gc{
    public $num;
    public function __construct($num)
    {
        $this->num=$num;
    }
    public function __destruct()
    {
        echo "Hello World!";
    }
}
$a = new gc(1);
$ser = serialize($a);
echo $ser;
$ser = 'O:2:"gc":1:{s:3:"num";i:1;';
$b = unserialize($ser);
throw new Exception("F12 is bad");

这里我们删去一个},依然输出了Hello World!

标签:refcount,destruct,num,GC,new,gc,PHP,ref,详解
From: https://www.cnblogs.com/F12-blog/p/17987039

相关文章

  • Unity3D 游戏中的自动寻路有怎样的算法详解
    前言Unity3D是一款非常流行的游戏引擎,它的自动寻路功能可以使游戏角色在场景中自动找到最短路径并前往目标位置。本文将详细介绍Unity3D中自动寻路的算法原理以及代码实现。对惹,这里有一个游戏开发交流小组,希望大家可以点击进来一起交流一下开发经验呀在游戏开发中,自动寻路是......
  • python中for循环及用法详解
    1、for循环for循环的语法格式如下:1for迭代变量in字符串|列表|元祖|字典|集合:2代码块迭代变量用于存放从序列类型变量中读取出来的元素,所以一般不会在循环中对迭代变量手动赋值;2、用法2.1、for循环遍历数值1print("计算1+2+3+...+100的结果为:")2sum=0......
  • 神经网络优化篇:详解为超参数选择合适的范围(Using an appropriate scale to pick hyper
    为超参数选择合适的范围假设要选取隐藏单元的数量\(n^{[l]}\),假设,选取的取值范围是从50到100中某点,这种情况下,看到这条从50-100的数轴,可以随机在其取点,这是一个搜索特定超参数的很直观的方式。或者,如果要选取神经网络的层数,称之为字母\(L\),也许会选择层数为2到4中的某个值,接着顺......
  • Unity3D Rts游戏里的群体移动算法是如何实现的详解
    实时战略(RTS)游戏是一种以管理和控制虚拟军队为主题的游戏类型。在这类游戏中,玩家需要控制大量的单位进行战斗、资源采集和建设等操作。其中,群体移动算法是实现这些操作的关键之一。本文将详细介绍Unity3DRTS游戏中群体移动算法的实现原理和代码实现。对啦!这里有个游戏开发交流小......
  • 用C++11打造智能观察者模式:详解实现步骤完整示例代码
     观察者模式是一种行为设计模式,其中一个对象(主题)维护其依赖对象(观察者)的列表,当主题的状态发生变化时,它通知所有观察者。以下是一个使用C++11实现观察者模式的简单例子:定义观察者接口(Observer): 创建一个观察者接口,该接口包含观察者需要实现的更新方法。这个接口可以包含其他......
  • 【C语言进阶篇】看完这篇结构体文章,我向数据结构又进了一大步!(结构体进阶详解)
    (文章目录)......
  • thinkphp 验证器
    方法一验证器的使用单独抽离成一个文件declare(strict_types=1);namespaceapp\validate;usethink\Validate;classValidateAllextendsValidate{/***定义验证规则*格式:'字段名'=>['规则1','规则2'...]**@vararray......
  • WGCLOUD接口监测多长时间扫描一次
    10分钟WGCLOUD的服务接口监测,默认10分钟监测一次,不过也可以修改在server配置文件里修改如下配置项即可,修改后重启下server就生效了#服务接口监控间隔,单位秒,默认10分钟heathTimes:600......
  • 关于php进行post出现500的超时问题解决办法
      最近搞个项目使用php进行post请求,时间长了就会出现500错误,ngnix报了个错误:upstreamtimedout(10060:Aconnectionattemptfailedbecausetheconnectedpartydidnotproperlyrespondafteraperiodoftime,orestablishedconnectionfailedbecauseconnected......
  • C# 方法详解:定义、调用、参数、默认值、返回值、命名参数、方法重载全解析
    C#Methods方法是一段代码,只有在调用时才会运行。您可以将数据(称为参数)传递给方法。方法用于执行某些操作,也被称为函数。为什么使用方法?为了重用代码:定义一次代码,然后多次使用。创建一个方法方法的定义以方法的名称开头,后跟括号()。C#提供了一些预定义的方法,您已经熟悉,例如M......