Утечка памяти при создании зацикленного объекта в расширении PHP7 - PullRequest
0 голосов
/ 05 апреля 2019

Ниже приведена созданная мной тестовая функция (для 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 голосов
/ 05 апреля 2019

Это связано с тем, что сборщик мусора 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

...