Skip to content

op_arrays with temporary run_time_cache leak memory when observed #8082

Closed
@bwoebi

Description

@bwoebi

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.

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions