Newsgroups: php.internals Path: news.php.net Xref: news.php.net php.internals:66185 Return-Path: Mailing-List: contact internals-help@lists.php.net; run by ezmlm Delivered-To: mailing list internals@lists.php.net Received: (qmail 15910 invoked from network); 25 Feb 2013 13:03:20 -0000 Received: from unknown (HELO lists.php.net) (127.0.0.1) by localhost with SMTP; 25 Feb 2013 13:03:20 -0000 Authentication-Results: pb1.pair.com header.from=Terry@ellisons.org.uk; sender-id=unknown Authentication-Results: pb1.pair.com smtp.mail=Terry@ellisons.org.uk; spf=permerror; sender-id=unknown Received-SPF: error (pb1.pair.com: domain ellisons.org.uk from 79.170.44.47 cause and error) X-PHP-List-Original-Sender: Terry@ellisons.org.uk X-Host-Fingerprint: 79.170.44.47 mail47.extendcp.co.uk Received: from [79.170.44.47] ([79.170.44.47:44640] helo=mail47.extendcp.co.uk) by pb1.pair.com (ecelerity 2.1.1.9-wez r(12769M)) with ESMTP id A6/A5-10787-5116B215 for ; Mon, 25 Feb 2013 08:03:18 -0500 Received: from host81-132-45-215.range81-132.btcentralplus.com ([81.132.45.215] helo=[192.168.1.91]) by mail47.extendcp.com with esmtpa (Exim 4.80.1) id 1U9xi5-0004b0-8r; Mon, 25 Feb 2013 13:03:13 +0000 Message-ID: <512B6110.6010901@ellisons.org.uk> Date: Mon, 25 Feb 2013 13:03:12 +0000 User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:17.0) Gecko/20130106 Thunderbird/17.0.2 MIME-Version: 1.0 To: Hans-Juergen Petrich CC: internals@lists.php.net References: In-Reply-To: Content-Type: multipart/alternative; boundary="------------030409020807010907070409" X-Authenticated-As: Terry@ellisons.org.uk Subject: Re: [PHP-DEV] (non)growing memory while creating anoymous functions via eval() From: Terry@ellisons.org.uk (Terry Ellison) --------------030409020807010907070409 Content-Type: text/plain; charset=UTF-8; format=flowed Content-Transfer-Encoding: 7bit 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( '|

\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 --------------030409020807010907070409--