PHP

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

More than 5 years have passed since last update.

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