On 03/02/13 15:27, Hans-Juergen Petrich wrote:
In this example (using php-5.4.11 on Linux) the memory will grow non-stop:

for ( $fp = fopen('/dev/urandom', 'rb'); true;) {
eval ('$ano_fnc = function() {$x = "'.bin2hex(fread($fp, mt_rand(1, 10000))).'";};');
    echo "Mem usage: ".memory_get_usage()."\n";
}


But in this example not:

for ( $fp = fopen('/dev/urandom', 'rb'); true;) {
eval ('$ano_fnc = function() {$x = "'.bin2hex(fread($fp, 10000)).'";};');
    echo "Mem usage: ".memory_get_usage()."\n";
}

Hans-Juergen, I've raised a bugrep https://bugs.php.net/bug.php?id=64291 which you might want to review and add any appropriate comments. I had to think about this one. It's worthwhile observing that this second example is the only occasion, as far as I know, that PHP does any garbage collection of code objects before request shutdown. For example create_function() objects are given the name "\0LambdaN" where N is the count of the number of created functions so far in this request. They are registered in the function table and persist until request shutdown. That's the way PHP handles them by design.

As I said in the bugrep, the normal behaviour of persistence is what you want because if you think about the normal use of the anonymous function, say

while (!feof($fp)) {
    $line = preg_replace_callback(
        '|<p>\s*\w|',
        function($matches) {return strtolower($matches[0]);},
        gets($fp)
    );
    echo $line;
}

Then the anonymous function is compiled once and rebound to the closure object which is passed as the second argument for the callback each time through the loop. OK, doing the closure CTOR/DTOR once per loop. is not the cleverest of ideas and this is the sort of thing that would be hoisted out of the loop in a language which did such optimization, but PHP doesn't. It's a LOT better that compiling a new function each loop (which is how Example #1 on http://php.net/manual/en/function.preg-replace-callback.php does it!) This is what you want to happen.

It's just too complicated for PHP to work out if the function might or not be rebound to. I suspect the bug here is really the implicit assumption that the magic function name generated by the

    eval ('$ano_fnc = function() { ... }');

is unique, but as your examples shows, thanks to garbage collection and reuse of memory, sometimes it isn't. In these circumstances thank to the use of a hash update and the table DTOR the old one is deleted.

So assume that what you are doing is exploiting a bug, so my advice is not to do this. It might be fixed in a future release.

Regards Terry

Reply via email to