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

我试图在 CLI 程序中创建一个类似“池”的结构,其中包括许多“借用”和“回收”。在测试的时候,我遇到了一些非常出乎意料的事情:


<?php

class 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";

输出是:


Bar

Got 2911

Recycling 2911

Bar

Got 2911

Bar

Got 1038

Recycling 1038

Bar

Got 1038

Array

(

)

End.

如果我将Bar()调用限制为 3 次而不是 4 次,我会得到以下输出:


Bar

Got 7278

Recycling 7278

Bar

Got 7278

Bar

Got 6703

Recycling 6703

Array

(

    [0] => FOO Object

        (

            [num] => 6703

        )


)

End.

在这里你可以看到,当一个对象被“重用”时(见例1中的2911和1038,例2中的7278),它__destruct()不会被调用;相反,它会简单地消失。


我很困惑。为什么会发生这种情况?为什么FOO->__destruct每次都不叫?是否可以制作FOO自动回收实例?


我在 PHP-7.2 中对此进行了测试,在 Windows 和 WSL-Ubuntu 中都观察到了这种行为。


湖上湖
浏览 73回答 1
1回答

子衿沉夜

据我所知,不会为同一个对象调用两次析构函数。重用析构函数中的对象通常是一种不好的做法。调用析构函数的对象应该被销毁,而不是重用。在您的测试脚本中它不会导致任何严重的问题,但在现实生活中,如果开发人员不小心,这种用法可能会导致意外行为。起初,当我阅读您的问题时,我担心您造成了内存泄漏,但事实并非如此。Bar()仅当尚未在此对象上调用析构函数时,才在离开范围时调用析构函数。但是,由于您保存了引用并在垃圾收集器ref_count内部增加了此对象的引用,__destruct()因此尚无法收集该对象。它必须等到下一次你减少ref_count这个对象的时候。该过程可以解释如下(简化):您调用Bar()创建一个实例的函数FOO。一个对象被创建并分配给$foo。(&nbsp;ref_count = 1)在结束时Bar()对该对象的唯一引用丢失(ref_count = 0),这意味着 PHP 开始销毁该对象的过程。2.1。析构函数被调用。在析构函数内部,您将ref_count增加到 1。2.2。下一步将是 GC 收集对象,但ref_count不是零。在您的示例中,这可能意味着循环或类似,在析构函数中创建了一个新引用。GC 必须等到没有非循环引用才能收集对象。你Bar()再打电话。您将同一个对象从静态数组中移出,这意味着其中的引用FOO::$pool已经消失,但您立即将其分配给$foo.&nbsp;(&nbsp;ref_count = 1)在结束时Bar()对该对象的唯一引用丢失(ref_count = 0),这意味着 GC 最终可以收集该对象。析构函数已经被调用了,所以这里不需要采取其他行动。你没有回收对象,你只是延长了它在内存中的存在。您可以访问它一段时间,但对于 PHP,该对象已经处于被销毁的过程中。
打开App,查看更多内容
随时随地看视频慕课网APP