-
Notifications
You must be signed in to change notification settings - Fork 7.8k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Combination of phpdbg and Generator method causes memory leak #12837
Comments
/cc @bwoebi |
This is not a memory leak, an error that used up all of the memory available to php. Most likely, the original code was designed to use relatively memory, and phpdbg used more memory, causing the memory limit to be exceeded. Try adding this at the beginning of your php code. I think the error will probably no longer occur.
|
Oops, the case of using phpdbg, you may should set the memory limit on command args.
|
No, it may be that for some reason object is not freed by garbage collector. <?php
class A
{
public function __construct()
{
var_dump('__construct');
}
public function __destruct()
{
var_dump('__destruct');
}
public function iterate()
{
yield 1;
}
}
$arr = iterator_to_array((new A())->iterate()); gc_collect_cycles();
$arr = iterator_to_array((new A())->iterate()); gc_collect_cycles();
$arr = iterator_to_array((new A())->iterate()); gc_collect_cycles();
var_dump('exit'); $ php zz.php
string(11) "__construct"
string(10) "__destruct"
string(11) "__construct"
string(10) "__destruct"
string(11) "__construct"
string(10) "__destruct"
string(4) "exit"
$ phpdbg -qrr zz.php
string(11) "__construct"
string(11) "__construct"
string(11) "__construct"
string(4) "exit"
string(10) "__destruct"
string(10) "__destruct"
string(10) "__destruct" |
There's some special handling of generators in phpdbg. It's possible that it keeps some reference around, preventing them from being freed. That does seem like a bug to me, but maybe there's a reason why the generators are kept around until the end of the script in this case. I don't know, hopefully @bwoebi can answer. |
Also, this problem does not occur if generator is function rather than object method. <?php
class A
{
public function __construct()
{
var_dump('__construct');
}
public function __destruct()
{
var_dump('__destruct');
}
public function iterate()
{
yield 1;
}
}
function iterate()
{
yield 1;
}
$arr = iterator_to_array(iterate(new A())); gc_collect_cycles();
$arr = iterator_to_array(iterate(new A())); gc_collect_cycles();
$arr = iterator_to_array(iterate(new A())); gc_collect_cycles();
var_dump('exit'); $ phpdbg -qrr zz.php
string(11) "__construct"
string(10) "__destruct"
string(11) "__construct"
string(10) "__destruct"
string(11) "__construct"
string(10) "__destruct"
string(4) "exit" |
|
Nice find! I think the original fix is simply wrong and we should just avoid the double destruction by checking for ZEND_CALL_TOP. I'll make a PR soon. |
…mory leak The fix for bug #72523 (d1dd474) added a check on `zend_execute_ex` to add an extra refcount to `This`. This is necessary because the VM does a re-entry here: https://github.com/php/php-src/blob/ecb90c1db7efe9d87c07e31336185fc393281035/Zend/zend_vm_def.h#L4294-L4299 and then cleans up `This` here: https://github.com/php/php-src/blob/ecb90c1db7efe9d87c07e31336185fc393281035/Zend/zend_vm_def.h#L4358-L4360 So if we don't add the refcount, we destroy `This` both in the VM and in `zend_generator_close`. The reason this causes a leak in phpdbg is because it changes the `zend_execute_ex` temporarily back to the default here for `ZEND_DO_FCALL`: https://github.com/php/php-src/blob/ecb90c1db7efe9d87c07e31336185fc393281035/sapi/phpdbg/phpdbg_prompt.c#L1820-L1827 which means that we only execute `OBJ_RELEASE` on the `This` object once in `zend_generator_close` instead of also doing it in the `ZEND_DO_FCALL` handler. To solve this, we make sure the check for the re-entrancy is consistently done by checking the `ZEND_CALL_TOP` flag instead of relying solely on `execute_ex` pointer: https://github.com/php/php-src/blob/ecb90c1db7efe9d87c07e31336185fc393281035/Zend/zend_vm_def.h#L4298
…mory leak The fix for bug #72523 (d1dd474) added a check on `zend_execute_ex` to add an extra refcount to `This`. This is necessary because the VM does a re-entry here: https://github.com/php/php-src/blob/ecb90c1db7efe9d87c07e31336185fc393281035/Zend/zend_vm_def.h#L4294-L4299 and then cleans up `This` here: https://github.com/php/php-src/blob/ecb90c1db7efe9d87c07e31336185fc393281035/Zend/zend_vm_def.h#L4358-L4360 So if we don't add the refcount, we destroy `This` both in the VM and in `zend_generator_close`. The reason this causes a leak in phpdbg is because it changes the `zend_execute_ex` temporarily back to the default here for `ZEND_DO_FCALL`: https://github.com/php/php-src/blob/ecb90c1db7efe9d87c07e31336185fc393281035/sapi/phpdbg/phpdbg_prompt.c#L1820-L1827 which means that we only execute `OBJ_RELEASE` on the `This` object once in `zend_generator_close` instead of also doing it in the `ZEND_DO_FCALL` handler. To solve this, we make sure the check for the re-entrancy is consistently done by checking the `ZEND_CALL_TOP` flag instead of relying solely on `execute_ex` pointer: https://github.com/php/php-src/blob/ecb90c1db7efe9d87c07e31336185fc393281035/Zend/zend_vm_def.h#L4298
…mory leak The fix for bug #72523 (d1dd474) added a check on `zend_execute_ex` to add an extra refcount to `This`. This is necessary because the VM does a re-entry here: https://github.com/php/php-src/blob/ecb90c1db7efe9d87c07e31336185fc393281035/Zend/zend_vm_def.h#L4294-L4299 and then cleans up `This` here: https://github.com/php/php-src/blob/ecb90c1db7efe9d87c07e31336185fc393281035/Zend/zend_vm_def.h#L4358-L4360 So if we don't add the refcount, we destroy `This` both in the VM and in `zend_generator_close`. The reason this causes a leak in phpdbg is because it changes the `zend_execute_ex` temporarily back to the default here for `ZEND_DO_FCALL`: https://github.com/php/php-src/blob/ecb90c1db7efe9d87c07e31336185fc393281035/sapi/phpdbg/phpdbg_prompt.c#L1820-L1827 which means that we only execute `OBJ_RELEASE` on the `This` object once in `zend_generator_close` instead of also doing it in the `ZEND_DO_FCALL` handler. To solve this, we make sure the check for the re-entrancy is consistently done by checking the `ZEND_CALL_TOP` flag instead of relying solely on `execute_ex` pointer: https://github.com/php/php-src/blob/ecb90c1db7efe9d87c07e31336185fc393281035/Zend/zend_vm_def.h#L4298
…mory leak The fix for bug #72523 (d1dd474) added a check on `zend_execute_ex` to add an extra refcount to `This`. This is necessary because the VM does a re-entry here: https://github.com/php/php-src/blob/ecb90c1db7efe9d87c07e31336185fc393281035/Zend/zend_vm_def.h#L4294-L4299 and then cleans up `This` here: https://github.com/php/php-src/blob/ecb90c1db7efe9d87c07e31336185fc393281035/Zend/zend_vm_def.h#L4358-L4360 So if we don't add the refcount, we destroy `This` both in the VM and in `zend_generator_close`. The reason this causes a leak in phpdbg is because it changes the `zend_execute_ex` temporarily back to the default here for `ZEND_DO_FCALL`: https://github.com/php/php-src/blob/ecb90c1db7efe9d87c07e31336185fc393281035/sapi/phpdbg/phpdbg_prompt.c#L1820-L1827 which means that we only execute `OBJ_RELEASE` on the `This` object once in `zend_generator_close` instead of also doing it in the `ZEND_DO_FCALL` handler. To solve this, we make sure the check for the re-entrancy is consistently done by checking the `ZEND_CALL_TOP` flag instead of relying solely on `execute_ex` pointer: https://github.com/php/php-src/blob/ecb90c1db7efe9d87c07e31336185fc393281035/Zend/zend_vm_def.h#L4298
Description
Run phpdbg on object has generator method causes memory leaks.
It does not occur when run with php command.
The following code:
Resulted in this output:
But I expected this output instead:
PHP Version
PHP 8.3.0
Operating System
Debian 12
The text was updated successfully, but these errors were encountered: