首页 文章

诊断内存泄漏 - 允许#bytes的内存大小耗尽

提问于
浏览
89

我遇到了可怕的错误消息,可能通过艰苦的努力,PHP内存不足:

允许的内存大小#### bytes耗尽(尝试分配####字节)在第123行的file.php中

增加限制

如果您知道自己在做什么并希望增加限制,请参阅memory_limit

ini_set('memory_limit', '16M');
ini_set('memory_limit', -1); // no limit

谨防!你可能只是解决症状而不是问题!

诊断泄漏:

错误消息指向一条带有循环的行,我认为该循环正在泄漏或不必要地累积内存 . 我在每次迭代结束时打印了 memory_get_usage() 语句,可以看到数字慢慢增长,直到达到限制:

foreach ($users as $user) {
    $task = new Task;
    $task->run($user);
    unset($task); // Free the variable in an attempt to recover memory
    print memory_get_usage(true); // increases over time
}

出于这个问题的目的,让我们假设可以想象的最糟糕的意大利面条代码隐藏在 $userTask 中的全局范围内 .

What tools, PHP tricks, or debugging voodoo can help me find and fix the problem?

13 回答

  • 1

    我很害怕,PHP没有很多代码可以通过搜索来找到它 . 最简单的工具是选择性地调用 memory_get_usage 并将其缩小到代码泄漏的位置 . 您还可以使用xdebug创建代码跟踪 . 使用execution tracesshow_mem_delta 运行代码 .

  • 7

    php中有几个可能的内存泄漏点:

    • php本身

    • php扩展

    • 你使用的php库

    • 你的PHP代码

    没有深度逆向工程或php源代码知识,很难找到并修复前3个 . 对于最后一个,您可以使用二进制搜索内存泄漏代码memory_get_usage

  • 45

    这是我们用来确定哪些脚本在我们的服务器上使用最多内存的技巧 .

    将以下代码段保存在文件中,例如 /usr/local/lib/php/strangecode_log_memory_usage.inc.php

    <?php
    function strangecode_log_memory_usage()
    {
        $site = '' == getenv('SERVER_NAME') ? getenv('SCRIPT_FILENAME') : getenv('SERVER_NAME');
        $url = $_SERVER['PHP_SELF'];
        $current = memory_get_usage();
        $peak = memory_get_peak_usage();
        error_log("$site current: $current peak: $peak $url\n", 3, '/var/log/httpd/php_memory_log');
    }
    register_shutdown_function('strangecode_log_memory_usage');
    

    通过在httpd.conf中添加以下内容来使用它:

    php_admin_value auto_prepend_file /usr/local/lib/php/strangecode_log_memory_usage.inc.php
    

    然后在 /var/log/httpd/php_memory_log 分析日志文件

    在Web用户可以写入日志文件之前,您可能需要 touch /var/log/httpd/php_memory_log && chmod 666 /var/log/httpd/php_memory_log .

  • 6

    我注意到在一个旧脚本中有一次,即使在我的foreach循环之后,PHP也会在范围内维护“as”变量 . 例如,

    foreach($users as $user){
      $user->doSomething();
    }
    var_dump($user); // would output the data from the last $user
    

    我已经看过了.2362107已经看过了 . 如果是这种情况,您可以在 doSomething() 行之后 unset($user) 从内存中清除它 . 因人而异 .

  • 8

    我最近在一个应用程序上遇到了这个问题,在我收集到的类似情况下 . 一个在PHP的cli中运行的脚本,它遍历许多迭代 . 我的脚本依赖于几个底层库 . 我怀疑某个特定的库是原因,我花了几个小时徒劳地试图在它的类中添加适当的析构方法无济于事 . 面对一个漫长的转换过程到一个不同的库(可能会遇到同样的问题)我想出了一个粗略的解决方案来解决我的问题 .

    在我的情况下,在linux cli上,我循环了一堆用户记录,并为每一个创建了我创建的几个类的新实例 . 我决定尝试使用PHP的exec方法创建类的新实例,以便这些进程可以在“新线程”中运行 . 这是我所指的一个非常基本的样本:

    foreach ($ids as $id) {
       $lines=array();
       exec("php ./path/to/my/classes.php $id", $lines);
       foreach ($lines as $line) { echo $line."\n"; } //display some output
    }
    

    显然这种方法有局限性,需要注意这种方法的危险,因为创建兔子工作很容易,但在极少数情况下,它可能有助于克服困难,直到找到更好的解决方案,就像我的情况一样 .

  • 0

    我遇到了同样的问题,我的解决方案是用常规替换foreach . 我不确定具体细节,但似乎foreach为对象创建了一个副本(或某种新的引用) . 使用常规for循环,您可以直接访问该项目 .

  • 6

    我最近注意到PHP 5.3 lambda函数在删除它们时会留下额外的内存 .

    for ($i = 0; $i < 1000; $i++)
    {
        //$log = new Log;
        $log = function() { return new Log; };
        //unset($log);
    }
    

    我不知道为什么,但是即使在删除函数之后,每个lambda似乎还需要250个字节 .

  • 2

    我建议你查看php手册或添加 gc_enable() 函数来收集垃圾......这就是内存泄漏不会影响代码的运行方式 .

    PS:php有一个不带参数的垃圾收集器 gc_enable() .

  • 11

    如果您在PHP函数为真后只对GC做了什么,那么可以将循环内容作为变通方法/实验包装在函数中 .

  • 3

    我遇到的一个大问题是使用create_function . 与lambda函数一样,它将生成的临时名称留在内存中 .

    内存泄漏的另一个原因(在Zend Framework的情况下)是Zend_Db_Profiler . 如果在Zend Framework下运行脚本,请确保已禁用 . 例如,我在我的application.ini中有以下内容:

    resources.db.profiler.enabled    = true
    resources.db.profiler.class      = Zend_Db_Profiler_Firebug
    

    在此之前运行大约25.000个查询加载处理,使内存达到了一个很好的128Mb(我的最大内存限制) .

    只需设置:

    resources.db.profiler.enabled    = false
    

    它足以使其低于20 Mb

    这个脚本在CLI中运行,但是它实例化了Zend_Application并运行了Bootstrap,所以它使用了“开发”配置 .

    它确实帮助运行脚本xDebug profiling

  • 2

    我没有看到它明确提到,但xdebug做了很好的分析时间 and memory (截至2.6) . 您可以获取它生成的信息并将其传递给您选择的gui前端:webgrind(仅限时间),kcachegrindqcachegrind或其他,它会生成非常有用的调用树和图形,以便您找到各种困境的来源 .

    示例(qcachegrind):
    enter image description here

  • 2

    我对这次谈话有点迟,但我会分享一些与Zend Framework相关的东西 .

    我安装了php 5.3.8(使用phpfarm)来处理使用php 5.2.9开发的ZF应用程序后出现内存泄漏问题 . 我发现内存泄漏是在Apache的httpd.conf文件中触发的,在我的虚拟主机定义中,它说的是 SetEnv APPLICATION_ENV "development" . 在评论此行后,内存泄漏停止 . 我正在尝试在我的php脚本中提出内联解决方法(主要是通过在主index.php文件中手动定义它) .

  • 3

    我没有在这里看到它,但有一点可能有用,就是使用xdebug和xdebug_debug_zval('variableName')来查看引用计数 .

    我还可以提供一个php扩展的例子:Zend Server的Z-Ray . 如果启用了数据收集,则内存使用将在每次迭代时生成气球,就像垃圾收集已关闭一样 .

相关问题