内存泄漏?!在'array_map'中使用'create_function'时垃圾收集器是否正确? [英] Memory leak?! Is Garbage Collector doing right when using 'create_function' within 'array_map'?

查看:182
本文介绍了内存泄漏?!在'array_map'中使用'create_function'时垃圾收集器是否正确?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我在StackOverflow上找到了以下解决方案,从对象数组中获取特定对象属性的数组: PHP - 从对象数组中提取属性



建议的解决方案是使用 array_map 并在内部用 create_function 创建一个函数,如下所示:

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

会发生什么?: array_map 数组元素在这种情况下是一个 stdClass 对象。首先它会创建一个如下所示的函数:

pre $ $ $ $ $ $ return $ o-> id;
}

其次,它在当前迭代中为该对象调用该函数。它的工作原理与以下类似的解决方案几乎相同:
$ b $ pre $ $ $ $ $ catIds = array_map(function($ o){return $ o - > id;},$ objects);

但是这个解决方案只能在PHP版本> = 5.3中运行,因为它使用 Closure concept => http://php.net/ manual / de / class.closure.php

现在真正的问题:

第一个使用 create_function 解决方案会增加内存,因为创建的函数将被写入内存,不会被重用或销毁。在第二种解决方案中, Closure 它会。



所以解决方案给出了相同的结果,但在尊重方面有不同的行为



以下示例:

  // //数组给出
$ 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;'), $对象);
// result:array(1,2,3);

echo memory_get_usage()。\\\
;

sleep(1);
}

4235616
4236600
4237560
4238520
...
$ b

GOOD

  while(true )
{
$ objects = array_map(function($ o){return $ o-> id;},$ objects);
// result:array(1,2,3);

echo memory_get_usage()。\\\
;

sleep(1);
}

4235136
4235168
4235168
4235168
...

我花了那么多时间来找出这个问题,现在我想知道,如果这是垃圾收集器的错误还是我犯了一个错误?
为什么在内存中保留已创建和调用的函数是有意义的,何时永远不会被重用?



这是一个运行示例: a href =http://ideone.com/9a1D5g =nofollow noreferrer> http://ideone.com/9a1D5g



已更新:当我递归搜索我的代码时,它的依赖关系如PEAR和Zend,然后我经常发现这个 BAD


$ b 更新:当两个函数嵌套时,为了评估这个表达,从内到外进行。换句话说,它首先启动 create_function (一次),并且返回的函数名称是 array_map 。但是因为GC忘记从内存中删除它(没有留下指向内存中的函数的指针),并且PHP不能重用已经存在于内存中的函数,所以我认为有一个错误,而不仅仅是一个性能不佳的东西, 。这个特定的代码行是PHPDoc中的一个例子,可以在很多大型框架中重用。 Zend和PEAR等等。多一行,你可以解决这个bug,检查。但我不是在寻找解决方案:我在寻找真相。这是一个错误还是只是我的方法。而后者我还没决定。

解决方案

create_function()使用 eval()创建一个lambda样式函数,并返回一个包含其名称的字符串。然后该名称作为参数传递给 array_map()函数。

这不同于封闭式匿名函数,其中根本不使用包含名称的字符串。 函数($ o){return $ o-> id; } IS 函数,或者说是Closure类的一个实例。 $ b

create_function()中的eval()函数执行一段创建想要的函数的PHP代码。有点像这样:

$ p $ 函数create_function($ arguments,$ code)
{
$ name =< ; _lambda_取代; //只是一个唯一的字符串
eval('function'。$ name。'($ arguments){$ code}');
返回$ name;
}

注意这是一个简化。



所以,一旦创建函数,它将一直持续到脚本结束,就像脚本中的普通函数一样。在上面的BAD示例中,在循环的每次迭代中创建一个新函数,占用越来越多的内存。



然而,您可以故意破坏拉姆达式功能。这很简单,只需将循环改为:

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

$ / code>

包含函数的引用(= name)的字符串被明确表示为在这里访问。现在,每次调用 create_function()时,旧函数都会被新函数覆盖。

不,没有'内存泄漏',它是以这种方式工作。



当然下面的代码更有效率:

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

while(true)
{
$ objects = array_map($ func,$ objects);
echo memory_get_usage()。\\\
;
sleep(1);
}

并且只能在您的封闭式匿名函数不受支持时使用PHP版本。


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

The proposed solution is to use array_map and within create a function with create_function as following:

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

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

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

Second it calls this function for the object in the current iteration. It works, it works nearly same like this similar solution:

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

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

Now the real problem:

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

So the solutions gives the same results but have different behaviors with respect to the memory.

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
...

GOOD

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 many time to find this out and now I want to know, if it's a bug with the garbage collector or do I made a mistake? And why it make sense to leave the already created and called function in memory, when it'll never be reuse?

Here is a running example: http://ideone.com/9a1D5g

Updated: When I recursively search my code and it's dependencies e.g. PEAR and Zend then I found this BAD way too often.

Updated: When two functions are nested, we proceed from the inside out in order to evaluate this expression. In other words, it is first starting create_function (once) and that returning function name is the argument for the single call of array_map. But because GC forget to remove it from memory (no pointer left to the function in memory) and PHP not be able to reuse the function already located in memory let me think that there is an error and not only a thing with "bad performance". This specific line of code is an example in PHPDoc and reused in so many big frameworks e.g. Zend and PEAR and more. With one line more you can work around this "bug", check. But I'm not searching for a solution: I'm searching for the truth. Is it a bug or is it just my approach. And latter I could not decide yet.

解决方案

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

This differs from the closure-style anonymous function where no string containing a name is used at all. function($o) { return $o->id; } IS the function, or rather an instance of the Closure class.

The eval() function, inside create_function(), executes a piece of PHP code which creates the wanted function. Somewhat like this:

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

Note that this is a simplification.

So, once the function is created it will persist until the end of the script, just like normal functions in a script. In the above BAD example, a new function is created like this on every iteration of the loop, taking up more and more memory.

You can, however, intentionally destroy the lambda-style function. This is quite easy, 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 string containting the reference (= name) to the function was made expliciet and accessible here. Now, every time create_function() is called, the old function is overwritten by a new one.

So, no, there's no 'Memory leak', it is meant to 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 should only be used when closure-style anonymous function are not supported by your PHP version.

这篇关于内存泄漏?!在'array_map'中使用'create_function'时垃圾收集器是否正确?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆