草庐IT

php - 内存泄漏?!在 'create_function' 中使用 'array_map' 时,垃圾收集器是否正常运行?

coder 2024-04-09 原文

我在 StackOverflow 上找到了以下解决方案,可以从对象数组中获取特定对象属性的数组:PHP - Extracting a property from an array of objects

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

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

会发生什么?:array_map 遍历每个数组元素,在本例中是一个 stdClass 对象。首先它创建一个这样的函数:

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

其次,它为当前迭代中的对象调用此函数。它有效,它的工作原理与类似的解决方案几乎相同:

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

但此解决方案仅在 PHP 版本 >= 5.3 中运行,因为它使用了 Closure 概念 => http://php.net/manual/de/class.closure.php

现在真正的问题是:

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

所以这些解决方案给出了相同的结果,但在内存方面具有不同的行为。

下面的例子:

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

不好

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

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

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

这是一个运行示例:http://ideone.com/9a1D5g

更新:当我递归搜索我的代码及其依赖项时,例如PEAR 和 Zend 然后我经常发现这种BAD方式。

更新:当两个函数嵌套时,我们从内向外进行以评估此表达式。换句话说,它首先启动 create_function(一次),返回的函数名称是 array_map 的单次调用的参数。但是因为 GC 忘记将它从内存中删除(没有指针留在内存中的函数)并且 PHP 无法重用已经位于内存中的函数让我认为存在错误而不仅仅是“性能差”的事情.这行特定的代码是 PHPDoc 中的示例,并在许多大型框架中重复使用,例如Zend 和 PEAR 等等。再多一行就可以解决这个“错误”,检查一下。但我不是在寻找解决方案:我在寻找真相。这是一个错误还是只是我的方法。后者我还不能决定。

最佳答案

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

这不同于闭包风格的匿名函数,后者根本不使用包含名称的字符串。 函数($o) { 返回$o->id; 函数,或者更确切地说是 Closure 类的一个实例。

eval() 函数在 create_function() 中,执行一段 PHP 代码来创建所需的函数。有点像这样:

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

请注意,这是一种简化。

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

但是,您可以故意破坏 lambda 风格的函数。这很简单,只需将循环更改为:

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

包含对函数的引用(= 名称)的字符串在这里是明确的和可访问的。现在,每次调用 create_function() 时,旧函数都会被新函数覆盖。

所以,不,没有“内存泄漏”,它本来就是这样工作的。

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

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

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

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

关于php - 内存泄漏?!在 'create_function' 中使用 'array_map' 时,垃圾收集器是否正常运行?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/25808390/

有关php - 内存泄漏?!在 'create_function' 中使用 'array_map' 时,垃圾收集器是否正常运行?的更多相关文章

  1. ruby - 如何从 ruby​​ 中的字符串运行任意对象方法? - 2

    总的来说,我对ruby​​还比较陌生,我正在为我正在创建的对象编写一些rspec测试用例。许多测试用例都非常基础,我只是想确保正确填充和返回值。我想知道是否有办法使用循环结构来执行此操作。不必为我要测试的每个方法都设置一个assertEquals。例如:describeitem,"TestingtheItem"doit"willhaveanullvaluetostart"doitem=Item.new#HereIcoulddotheitem.name.shouldbe_nil#thenIcoulddoitem.category.shouldbe_nilendend但我想要一些方法来使用

  2. ruby-on-rails - rails : "missing partial" when calling 'render' in RSpec test - 2

    我正在尝试测试是否存在表单。我是Rails新手。我的new.html.erb_spec.rb文件的内容是:require'spec_helper'describe"messages/new.html.erb"doit"shouldrendertheform"dorender'/messages/new.html.erb'reponse.shouldhave_form_putting_to(@message)with_submit_buttonendendView本身,new.html.erb,有代码:当我运行rspec时,它失败了:1)messages/new.html.erbshou

  3. ruby-on-rails - 由于 "wkhtmltopdf",PDFKIT 显然无法正常工作 - 2

    我在从html页面生成PDF时遇到问题。我正在使用PDFkit。在安装它的过程中,我注意到我需要wkhtmltopdf。所以我也安装了它。我做了PDFkit的文档所说的一切......现在我在尝试加载PDF时遇到了这个错误。这里是错误:commandfailed:"/usr/local/bin/wkhtmltopdf""--margin-right""0.75in""--page-size""Letter""--margin-top""0.75in""--margin-bottom""0.75in""--encoding""UTF-8""--margin-left""0.75in""-

  4. ruby-on-rails - 'compass watch' 是如何工作的/它是如何与 rails 一起使用的 - 2

    我在我的项目目录中完成了compasscreate.和compassinitrails。几个问题:我已将我的.sass文件放在public/stylesheets中。这是放置它们的正确位置吗?当我运行compasswatch时,它不会自动编译这些.sass文件。我必须手动指定文件:compasswatchpublic/stylesheets/myfile.sass等。如何让它自动运行?文件ie.css、print.css和screen.css已放在stylesheets/compiled。如何在编译后不让它们重新出现的情况下删除它们?我自己编译的.sass文件编译成compiled/t

  5. ruby - 如何每月在 Heroku 运行一次 Scheduler 插件? - 2

    在选择我想要运行操作的频率时,唯一的选项是“每天”、“每小时”和“每10分钟”。谢谢!我想为我的Rails3.1应用程序运行调度程序。 最佳答案 这不是一个优雅的解决方案,但您可以安排它每天运行,并在实际开始工作之前检查日期是否为当月的第一天。 关于ruby-如何每月在Heroku运行一次Scheduler插件?,我们在StackOverflow上找到一个类似的问题: https://stackoverflow.com/questions/8692687/

  6. ruby-on-rails - Rails 3.2.1 中 ActionMailer 中的未定义方法 'default_content_type=' - 2

    我在我的项目中添加了一个系统来重置用户密码并通过电子邮件将密码发送给他,以防他忘记密码。昨天它运行良好(当我实现它时)。当我今天尝试启动服务器时,出现以下错误。=>BootingWEBrick=>Rails3.2.1applicationstartingindevelopmentonhttp://0.0.0.0:3000=>Callwith-dtodetach=>Ctrl-CtoshutdownserverExiting/Users/vinayshenoy/.rvm/gems/ruby-1.9.3-p0/gems/actionmailer-3.2.1/lib/action_mailer

  7. ruby-on-rails - 如何在 ruby​​ 中使用两个参数异步运行 exe? - 2

    exe应该在我打开页面时运行。异步进程需要运行。有什么方法可以在ruby​​中使用两个参数异步运行exe吗?我已经尝试过ruby​​命令-system()、exec()但它正在等待过程完成。我需要用参数启动exe,无需等待进程完成是否有任何ruby​​gems会支持我的问题? 最佳答案 您可以使用Process.spawn和Process.wait2:pid=Process.spawn'your.exe','--option'#Later...pid,status=Process.wait2pid您的程序将作为解释器的子进程执行。除

  8. ruby - 无法运行 Rails 2.x 应用程序 - 2

    我尝试运行2.x应用程序。我使用rvm并为此应用程序设置其他版本的ruby​​:$rvmuseree-1.8.7-head我尝试运行服务器,然后出现很多错误:$script/serverNOTE:Gem.source_indexisdeprecated,useSpecification.Itwillberemovedonorafter2011-11-01.Gem.source_indexcalledfrom/Users/serg/rails_projects_terminal/work_proj/spohelp/config/../vendor/rails/railties/lib/r

  9. ruby - 在 jRuby 中使用 'fork' 生成进程的替代方案? - 2

    在MRIRuby中我可以这样做:deftransferinternal_server=self.init_serverpid=forkdointernal_server.runend#Maketheserverprocessrunindependently.Process.detach(pid)internal_client=self.init_client#Dootherstuffwithconnectingtointernal_server...internal_client.post('somedata')ensure#KillserverProcess.kill('KILL',

  10. ruby - 主要 :Object when running build from sublime 的未定义方法 `require_relative' - 2

    我已经从我的命令行中获得了一切,所以我可以运行rubymyfile并且它可以正常工作。但是当我尝试从sublime中运行它时,我得到了undefinedmethod`require_relative'formain:Object有人知道我的sublime设置中缺少什么吗?我正在使用OSX并安装了rvm。 最佳答案 或者,您可以只使用“require”,它应该可以正常工作。我认为“require_relative”仅适用于ruby​​1.9+ 关于ruby-主要:Objectwhenrun

随机推荐