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