为了账号安全,请及时绑定邮箱和手机立即绑定

重用的对象不会调用`__destruct`?

重用的对象不会调用`__destruct`?

PHP
湖上湖 2022-07-02 16:45:06
我试图在 CLI 程序中创建一个类似“池”的结构,其中包括许多“借用”和“回收”。在测试的时候,我遇到了一些非常出乎意料的事情:<?phpclass FOO{    public static $pool=[];    public static function get()    {        if(empty(self::$pool))        {            self::$pool[]=new self(mt_rand(1000,9999));        }        return array_shift(self::$pool);    }    protected static function recycle(FOO $foo)    {        echo "Recycling {$foo->num}\n";        self::$pool[]=$foo;    }    public $num;    protected function __construct(int $num)    {        $this->num=$num;    }    public function __destruct()    {        static::recycle($this);    }}function Bar(){    $foo=FOO::get();    echo "Got {$foo->num}\n";}echo "Bar\n";Bar();echo "Bar\n";Bar();echo "Bar\n";Bar();echo "Bar\n";Bar();print_r(FOO::$pool);echo "End.\n";输出是:BarGot 2911Recycling 2911BarGot 2911BarGot 1038Recycling 1038BarGot 1038Array()End.如果我将Bar()调用限制为 3 次而不是 4 次,我会得到以下输出:BarGot 7278Recycling 7278BarGot 7278BarGot 6703Recycling 6703Array(    [0] => FOO Object        (            [num] => 6703        ))End.在这里你可以看到,当一个对象被“重用”时(见例1中的2911和1038,例2中的7278),它__destruct()不会被调用;相反,它会简单地消失。我很困惑。为什么会发生这种情况?为什么FOO->__destruct每次都不叫?是否可以制作FOO自动回收实例?我在 PHP-7.2 中对此进行了测试,在 Windows 和 WSL-Ubuntu 中都观察到了这种行为。
查看完整描述

1 回答

?
子衿沉夜

TA贡献1828条经验 获得超3个赞

据我所知,不会为同一个对象调用两次析构函数。重用析构函数中的对象通常是一种不好的做法。调用析构函数的对象应该被销毁,而不是重用。在您的测试脚本中它不会导致任何严重的问题,但在现实生活中,如果开发人员不小心,这种用法可能会导致意外行为。

起初,当我阅读您的问题时,我担心您造成了内存泄漏,但事实并非如此。Bar()仅当尚未在此对象上调用析构函数时,才在离开范围时调用析构函数。但是,由于您保存了引用并在垃圾收集器ref_count内部增加了此对象的引用,__destruct()因此尚无法收集该对象。它必须等到下一次你减少ref_count这个对象的时候。

该过程可以解释如下(简化):

  1. 您调用Bar()创建一个实例的函数FOO。一个对象被创建并分配给$foo。( ref_count = 1)

  2. 在结束时Bar()对该对象的唯一引用丢失(ref_count = 0),这意味着 PHP 开始销毁该对象的过程。
    2.1。析构函数被调用。在析构函数内部,您将ref_count增加到 1。
    2.2。下一步将是 GC 收集对象,但ref_count不是零。在您的示例中,这可能意味着循环或类似,在析构函数中创建了一个新引用。GC 必须等到没有非循环引用才能收集对象。

  3. Bar()再打电话。您将同一个对象从静态数组中移出,这意味着其中的引用FOO::$pool已经消失,但您立即将其分配给$foo. ( ref_count = 1)

  4. 在结束时Bar()对该对象的唯一引用丢失(ref_count = 0),这意味着 GC 最终可以收集该对象。析构函数已经被调用了,所以这里不需要采取其他行动。

你没有回收对象,你只是延长了它在内存中的存在。您可以访问它一段时间,但对于 PHP,该对象已经处于被销毁的过程中。


查看完整回答
反对 回复 2022-07-02
  • 1 回答
  • 0 关注
  • 72 浏览

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信