在PHP7扩展中创建循环对象时发生内存泄漏

时间:2019-04-05 12:28:29

标签: php c php-extension

以下是我创建的测试功能(针对PHP 7.1)。

PHP_FUNCTION(tsc_test3)
{
    zend_string *cnA;
    zend_class_entry *ceA;

    // $ret = new ClsA();
    cnA = zend_string_init("ClsA", 4, 0);
    ceA = zend_fetch_class(cnA, ZEND_FETCH_CLASS_DEFAULT);
    zend_string_release(cnA);
    object_init_ex(return_value, ceA);

    // $ret->propA = $ret;
    zval objA;
    ZVAL_COPY(&objA, return_value);
    zend_update_property(ceA, return_value, "propA", 5, &objA);
    zval_ptr_dtor(&objA);

    return;
}

如注释中所建议,它返回ClsA的循环对象。 以下是该功能的测试PHP程序。

<?php
class ClsA {
    public $propA = 1;
}

$x = tsc_test3();

echo "DUMP1 ----\n";
var_dump($x);

for ($i = 0; $i < 10; $i++) {
    echo "Memory usage: ". memory_get_usage(). "\n";
    $x = tsc_test3();
}

echo "DUMP2 ----\n";
var_dump($x);

$x->propA = null;

echo "DUMP3 ----\n";
var_dump($x);

这是PHP代码的输出。

DUMP1 ----
object(ClsA)#1 (1) {
  ["propA"]=>
  *RECURSION*
}
Memory usage: 351336
Memory usage: 351392
Memory usage: 351448
Memory usage: 351504
Memory usage: 351560
Memory usage: 351616
Memory usage: 351672
Memory usage: 351728
Memory usage: 351784
Memory usage: 351840
DUMP2 ----
object(ClsA)#11 (1) {
  ["propA"]=>
  *RECURSION*
}
DUMP3 ----
object(ClsA)#11 (1) {
  ["propA"]=>
  NULL
}

var_dump()的结果看起来不错,但是内存使用量不断增加。

当我使用ZVAL_COPY_VALUE而不是ZVAL_COPY时,内存使用并没有增加,但是在DUMP3中却产生了奇怪的输出。

DUMP3 ----
*RECURSION*

函数可能返回损坏的对象。

有人可以告诉我扩展功能有什么问题吗?

Edit1:发布问题后,我注意到memory_get_usage(true)并没有增加。这是我犯的错误吗?

Edit2:以下PHP程序(纯PHP,无扩展名)显示内存使用增加。这是PHP错误还是我误解了?我正在使用PHP 7.1.28。

<?php
class ClsA {
    public $propA = 1;
}

for ($i = 0; $i < 10; $i++) {
    echo "Memory usage: ". memory_get_usage(). "\n";
    $x = new ClsA();
    $x->propA = $x;
}

1 个答案:

答案 0 :(得分:0)

这是由于以下事实:在分配引用的确切时刻,PHP垃圾收集器并不总是回收对象(ClsA实例)使用的内存( $x)到另一个对象。 PHP完全使用了引用计数和垃圾回收。

如果在每个循环上强制进行垃圾回收,则会看到内存占用量保持不变:

<?php
class ClsA {
    public $propA = 1;
}

for ($i = 0; $i < 10; $i++) {
    gc_collect_cycles();
    echo "Memory usage: ". memory_get_usage(). "\n";
    $x = new ClsA();
    $x->propA = $x;
}

输出:

$ php test.php
Memory usage: 396296 <-- before the first ClsA allocation
Memory usage: 396384
Memory usage: 396384
Memory usage: 396384
Memory usage: 396384
Memory usage: 396384
Memory usage: 396384
Memory usage: 396384
Memory usage: 396384
Memory usage: 396384

此处的更多(技术)信息:https://www.php.net/manual/en/features.gc.collecting-cycles.php