Memory leak?! Is the garbage collector correct using 'create_function' in 'array_map'?

I found the following solution here in StackOverflow to get an array of a specific property of an object from an array of objects: PHP - Extracting a property from an array of objects

The proposed solution is to use array_map and inside create a function with create_function as follows:

 $catIds = array_map(create_function('$o', 'return $o->id;'), $objects); 

What happens ?: array_map runs through each element of the array in this case the stdClass object. First, he creates a function like this:

 function($o) { return $o->id; } 

He then calls this function on the object in the current iteration. It works, it works almost the same as this similar solution:

 $catIds = array_map(function($o) { return $o->id; }, $objects); 

But this solution only works in PHP version> = 5.3, because it uses Closure concept => http://php.net/manual/de/class.closure.php

Now the real problem:

The first solution with create_function increases memory because the created function will be written to memory and will not be reused or destroyed. In the second decision with Closure it will be.

Thus, the solutions give the same results, but have different behavior with respect to memory.

The following example:

 // following array is given $objects = array ( [0] => stdClass ( [id] => 1 ), [1] => stdClass ( [id] => 2 ), [2] => stdClass ( [id] => 3 ) ) 

Bad

 while (true) { $objects = array_map(create_function('$o', 'return $o->id;'), $objects); // result: array(1, 2, 3); echo memory_get_usage() ."\n"; sleep(1); } 4235616 4236600 4237560 4238520 ... 

OK

 while (true) { $objects = array_map(function($o) { return $o->id; }, $objects); // result: array(1, 2, 3); echo memory_get_usage() ."\n"; sleep(1); } 4235136 4235168 4235168 4235168 ... 

I spend so much time to find out, and now I want to know if this is a bug with the garbage collector or did I make a mistake? And why does it make sense to leave an already created and called function in memory when it will never be reused?

Here is an example: http://ideone.com/9a1D5g

Updated . When I recursively scan my code and its dependencies, for example. PEAR and Zend, I have too often found this BAD .

Updated . When two functions are nested, we proceed from within to evaluate this expression. In other words, it first runs create_function (once), and the return function name is an argument for a single call to array_map . But since the GC forgets to delete it from memory (no pointer is left for the function in memory), and PHP cannot reuse the function already in memory, let me think that there is an error, and not just the thing with "poor performance", This particular line of code is an example in PHPDoc and is reused in many large frameworks, for example. Zend and PEAR and more. With one line you can get around this "error", check. But I am not looking for a solution: I am looking for the truth. Is this a mistake, or is this my approach. And the last I could not decide.

+3
garbage-collection php memory-leaks array-map
Sep 12 '14 at 12:29
source share
3 answers

In the case of create_function() , the lambda-style function is created using eval() , and a string containing its name is returned. This name is then passed as an argument to the array_map() function.

This differs from an anonymous function such as closing, when not a single line containing a name is used at all. function($o) { return $o->id; } function($o) { return $o->id; } IS function, or rather, an instance of the Closure class.

The eval() function, inside create_function() , executes a piece of PHP code that creates the desired function. More or less like this:

 function create_function($arguments,$code) { $name = <_lambda_>; // just a unique string eval('function '.$name.'($arguments){$code}'); return $name; } 

Please note that this is a simplification.

So, as soon as the function is created, it will be saved until the end of the script, like ordinary functions in the script. In the BAD example above, at each iteration of the loop, a new function is created that takes up more and more memory.

However, you can intentionally destroy the lambda-style function. This is pretty simple, just change the loop to:

 while (true) { $func = create_function('$o', 'return $o->id;'); $objects = array_map($func, $objects); echo memory_get_usage() ."\n"; sleep(1); } 

The line containing the link (= name) of the function was made by expliciet and is available here. Now, every time create_function() is create_function() , the old function is overwritten with the new one.

So no, there is no "memory leak", it should work this way.

Of course, the code below is more efficient:

 $func = create_function('$o', 'return $o->id;'); while (true) { $objects = array_map($func, $objects); echo memory_get_usage() ."\n"; sleep(1); } 

And it should be used only when the anonymous close function is not supported by your version of PHP.

+9
Sep 15 '14 at 9:30
source share

Do not use create_function() if you can avoid this. In particular, more than once. In the big yellow Caution in the PHP Manual :

... it has poor performance and memory usage characteristics.

+3
Sep 15 '14 at 9:19
source share

Well, I think the problem is that the first solution with create_function works on older versions of PHP, and the second solution does not increase unnecessary memory. But let's take a look at the first solution. The create_function method create_function called inside array_map , namely for each while iteration. If we want the solution to work with older versions of PHP and without increasing the amount of memory, we must do the following for an older instance of the function at each while iteration:

 $func = create_function('$o', 'return $o->id;'); $catIds = array_map($func, $objects); 

It's all. So simple.

But he also does not answer the question at all. The question remains, is this a bug with PHP or a function. For my understanding, the way to write the result of create_function in a variable MUST be the same as putting it directly as a parameter in array_map , right?

+2
Sep 15 '14 at 9:07
source share



All Articles