5
5

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

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

Last updated at Posted at 2013-10-04

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クラスは要注意です。

5
5
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
5
5

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?