5
Help us understand the problem. What are the problem?

More than 5 years have passed since last update.

posted at

updated at

Organization

SPLのコンテナクラスで循環参照するとGCで回収されない

Zend Framework 2 を弄っていて SplPriorityQueue に気になる不具合があることがわかりました。

下記の Bug の通りですが、SPL の幾つかのコンテナクラスで循環参照するとGCで回収されません。

次のようなコードで確認できます。

SplCircularReferenceTest.php
<?php
class TestClass
{
    public function __destruct()
    {
        SplCircularReferenceTest::$flg = true;
    }
}

class SplCircularReferenceTest extends \PHPUnit_Framework_TestCase
{
    public static $flg = false;

    function testArray()
    {
        self::$flg = false;

        call_user_func(function () {
            $aa = new TestClass();
            $bb = array($aa);
            $aa->bb = $bb;
        });

        gc_collect_cycles();
        $this->assertTrue(self::$flg);
    }

    function testObject()
    {
        self::$flg = false;

        call_user_func(function () {
            $aa = new TestClass();
            $bb = new \stdClass();
            $bb->aa = $aa;
            $aa->bb = $bb;
        });

        gc_collect_cycles();
        $this->assertTrue(self::$flg);
    }

    function testSplFixedArray()
    {
        self::$flg = false;

        call_user_func(function () {
            $aa = new TestClass();
            $bb = new \SplFixedArray(1);
            $bb[0] = $aa;
            $aa->bb = $bb;
        });

        gc_collect_cycles();
        $this->assertTrue(self::$flg);
    }

    function testSplObjectStorage()
    {
        self::$flg = false;

        call_user_func(function () {
            $aa = new TestClass();
            $bb = new \SplObjectStorage();
            $bb->attach($aa);
            $aa->bb = $bb;
        });

        gc_collect_cycles();
        $this->assertTrue(self::$flg);
    }

    function testArrayObject()
    {
        self::$flg = false;

        call_user_func(function () {
            $aa = new TestClass();
            $bb = new \ArrayObject([$aa]);
            $aa->bb = $bb;
        });

        gc_collect_cycles();
        //$this->assertTrue(self::$flg);
    }

    function testSplDoublyLinkedList()
    {
        self::$flg = false;

        call_user_func(function () {
                $aa = new TestClass();
                $bb = new \SplDoublyLinkedList();
                $bb->push($aa);
                $aa->bb = $bb;
            });

        gc_collect_cycles();
        //$this->assertTrue(self::$flg);
    }

    function testSplMaxHeap()
    {
        self::$flg = false;

        call_user_func(function () {
                $aa = new TestClass();
                $bb = new \SplMaxHeap();
                $bb->insert($aa);
                $aa->bb = $bb;
            });

        gc_collect_cycles();
        //$this->assertTrue(self::$flg);
    }

    function testSplPriorityQueue()
    {
        self::$flg = false;

        call_user_func(function () {
            $aa = new TestClass();
            $bb = new \SplPriorityQueue();
            $bb->insert($aa, 1);
            $aa->bb = $bb;
        });

        gc_collect_cycles();
        //$this->assertTrue(self::$flg);
    }
}

SplCircularReferenceTest::$flg は TestClass のデストラクタで true になることを期待しています。

各テストメソッド内で TestClass と配列やオブジェクトのインスタンスが循環参照していますが、gc_collect_cycles() を明示的に呼び出しているので解放されるはずです。

が、ArrayObject を含む幾つかのSPLクラスでオブジェクトが解放されませんでした。

SplFixedArray と SplObjectStorage は大丈夫ですが、その他のSPLクラスは要注意です。

Register as a new user and use Qiita more conveniently

  1. You can follow users and tags
  2. you can stock useful information
  3. You can make editorial suggestions for articles
What you can do with signing up
5
Help us understand the problem. What are the problem?