Description
Description
The following code leaks memory when observed:
<?php
$closure = function() { /* observe this */ };
$object = new class {};
$mem = memory_get_usage();
while (true) {
if ($mem != memory_get_usage()) {
var_dump($mem = memory_get_usage());
}
$closure->call($object);
}
To reproduce, run with php -dzend_test.observer.enabled=1 -dzend_test.observer.observe_all=1 -dzend_test.observer.show_output=0
and see memory consumption explode.
PHP Version
PHP8.0 - master
Analysis
Observing op_arrays arena allocates some memory, which is never freed, even if the specific op_array (or more precisely run_time_cache) is freed.
We are constrained by the fact that run_time_cache is caller managed memory currently. Doing something else is an ABI or API break and not viable in PHP 8.0 or 8.1.
The only solution which comes to my mind is allocating enough space in the run_time_cache (e.g. at its end) to fit handlers there, i.e. 16 bytes (two pointers, start and end) per registered observing initializer instead of arena allocating that memory. This would be achieved by an increase of cache_size by the appropriate amount then.
This should also be safe regarding the application interface, especially given that the structs are not exported.
For PHP 8.2 one might consider a cleaner solution then.