The following is the test function I created (for 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;
}
As suggested in the comment, it returns a cycled object of ClsA
.
The following is the test PHP program for the function.
<?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);
Here is the output of the PHP code.
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 }
The var_dump()
result looks fine, but memory usage constantly increases.
When I use ZVAL_COPY_VALUE
instead of ZVAL_COPY
, the memory usage doesn't increase, but it produces a weird output in DUMP3.
DUMP3 ---- *RECURSION*
May be the function returns a corrupted object.
Can anybody tell me what's wrong in the extension function?
Edit1: Just after posting the question I noticed memory_get_usage(true)
doesn't increase. Is this the mistake I made?
Edit2: The following PHP program (pure PHP, no extension) shows increasing memory usage. Is this a PHP bug or am I misunderstanding something? I'm using 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;
}
This is due to the fact that the PHP garbage collector doesn't always reclaim the memory used by your object (a ClsA
instance) at the exact moment you assign your reference ($x
) to another object. PHP uses ref counting and garbage collection altogether.
If you force a garbage collection on each loop, you will see that your memory footprint will remain the same :
<?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;
}
Output :
$ 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
More (technical) info here : https://www.php.net/manual/en/features.gc.collecting-cycles.php