【问题标题】:Fatal error: Out of memory, but I do have plenty of memory (PHP)致命错误:内存不足,但我确实有足够的内存(PHP)
【发布时间】:2012-08-14 11:30:10
【问题描述】:

由于我的问题越来越长,我决定重写整个问题以使其更好更短。

我在具有 8GB 内存的专用服务器上运行我的网站。我完全知道我需要提高 php.ini 设置的内存限制。我已将其从 128M 设置为 256M 和 -1。问题仍然是持久性。

致命错误:内存不足(已分配 786432)(试图分配 24576 字节)在 D:\www\football\views\main.php 第 81 行

内存不足没有意义,因为它说只分配了 786432 字节,而且还需要 24576 字节。

786432 字节只有 768 KB,而且相当小。

提示

  • 错误发生在非常随机的行上。它并不总是在第 81 行出错。
  • 在高峰期,Apache 只占用大约 500mb 的内存。我还有 6GB 可用空间。
  • 没有无限循环。
  • 脚本占用 1,042,424 字节。从echo memory_get_peak_usage(); 获取此号码
  • MySQL 的结果集很小(最多 12 行,纯文本,无 blob 数据)
  • 重要)如果我每两天重新启动一次Apache,错误就消失了。这通常发生在 Apache 运行超过 2 天时。
  • 我已包含分析脚本,您可以通过here 获取它。
  • 这个专用服务器纯粹是用来运行一个网站的。该网站是一个高流量网站,平均每分钟有 1,000 名访问者。在高峰期,将有 1,700 到 2,000 名访客同时访问。

服务器规格

操作系统:Windows 2008 R2 64 位
CPU:英特尔酷睿 i5 - 4 核
内存:8 GB
阿帕奇 2.2
PHP 5.3.1
存储:2 x 1 TB 硬盘
带宽:每月 10 TB

解决方案

我终于调整并解决了这个问题,我想在这里分享我为改进所做的工作:

  1. favicon.ico 丢失了,这弄乱了我的路由引擎。虽然我的路由引擎很小,但是通过包含favicon.ico,它有助于通过不运行我的路由引擎来减少内存使用。我网站的大部分部分都有它,但我忘了把它放在这个新部分。
  2. 限制MaxRequestPerChild 有帮助。在我的另一台专用服务器中,我的MaxRequestPerChild 有限。对于这个服务器,我设置为0。我一直以为每个脚本都是隔离的。假设我的脚本需要 800kb 才能运行。完成后,Apache 或 PHP 应该释放 800kb 内存。似乎它不以这种方式工作。有限MaxRequestPerChild 确实有助于防止内存泄漏,方法是在有限MaxRequestPerChild 之后创建新进程并且旧进程正在死亡。这是我的新设置。

    ThreadsPerChild      1500
    MaxRequestsPerChild  10000 
    
  3. ob_flush(); 确实会稍微减少更多内存。它没有多大帮助,但每一点优化都有帮助。

  4. 我使用了xdebug,正如试图回答这个问题的人所建议的那样,我以前从未使用过。我不得不说它是一个很棒的工具,我已经优化了一些东西,让它运行得更快。
  5. 我禁用了一些不必要的 Apache 模块。我正在尝试一一禁用它,并对其进行几天的测试,以确保它在禁用另一个之前完美运行。我现在确实禁用了所有不必要的 PHP 扩展。
  6. 我在此服务器中的大部分脚本都使用传统方式(无模板、无数据库层、纯 PHP、HTML 和遗留 mysql_* 函数)。老实说,它运行速度非常快,而且使用的内存非常小。但是,由于网站越来越长,维护脚本并不容易。我试图将网站的某些部分转换为适当的框架(我自己的小框架)。我使用自己的框架的原因是它很小(整个框架只有 3kb,只包含我需要的内容)。
  7. 切换到 IIS7.5 彻底解决了这个问题

【问题讨论】:

  • 我闻到无限循环的味道。 :) 什么版本的 PHP?以及第 81 行的代码怎么样。
  • 绝对值得支持 Leigh /\
  • 仅仅因为它在 70% 的时间内都有效,并不意味着某些输入或变量不会使火车驶离轨道并杀死村民。我相当肯定火车是一个无限循环。 :D
  • 我记得也遇到过类似的问题,是因为我使用了一个循环,使用doctrine往数据库中插入了一些数据,每次迭代,他都在数据库中打开了一个游标!
  • 让我们开箱即用地思考一下。你对apache进程有任何内存限制吗?是否有任何 ulimit 有效(可能是 www 用户或 PHP 脚本的所有者,例如在 .profile 中)?您是否尝试过在 Apache 模块中停止工作后立即在 CLI 中运行该脚本?您是否使用任何缓存机制 PHP 模块?您是否使用 suhosin 或类似的强化模块?

标签: php memory


【解决方案1】:

我会先将 PHP 升级到 5.4+,因为对于某些应用程序,它的速度提高了 50%。 他们修复了大量的内存泄漏。请看becnhamrks:http://news.php.net/php.internals/57760

【讨论】:

  • 如果一切都失败了,升级到 PHP 5.4+ 将是我最后的选择。谢谢。
【解决方案2】:

安装 xdebug 并启用分析器触发器。 生成一个分析器文件,然后如果您仍然无法确定问题的根源,请发布 cachegrind 文件。

编辑:当然是发生内存泄漏的页面的分析器文件!

【讨论】:

  • 说实话,xdebug我没用过,不过我会试一试,以后会反馈给大家的。
  • 如果您逐步发布如何从头到尾“执行该过程”,您可能会很好。
【解决方案3】:

以下两个事实肯定指向内存泄漏:

  1. 错误出现在代码的不同行,
  2. 错误报告内存分配相对较小。

我会首先选择 PDO,禁用所有其他扩展,然后使用 Siege / Apache Bench (ab) 之类的工具让它在一夜之间运行。您也可以尝试使用cli 接口运行它(只要确保保持相同的内存限制)。

您可以在脚本末尾使用 memory_get_peak_usage() 函数来查看 PHP 认为它已经使用了多少内存。

从您的评论来看,这是 800 kB,没关系;绝对不是会导致内存不足的大量内存;-)

最后,虽然我现在不建议升级到 5.4,但升级到最新的 5.3.x 可能是值得的,因为自 5.3.1 以来已经解决了多个漏洞和泄漏

【讨论】:

  • 感谢您关注我的问题。一个upvote作为奖励。我使用过memory_get_peak_usauge(),每次使用大约 800kb。我已经调整了不同的配置,稍后我会更新我观察到的内容。
  • @invisal 忘了问你;当您收到错误时,apache 是否仍然只有 500MB?我想知道您是否已经预先分叉,是否可以配置另一个内存限制:) 当然,在您的 php 上设置最大请求数也有助于限制内存
【解决方案4】:

我会说服务器的物理/交换内存用完了,所以 PHP 无法分配足够的内存。

你能把free的输出粘贴到这里吗?

【讨论】:

    【解决方案5】:

    对于初学者,memory_get_peak_usage() 在这里没有帮助。它只会返回分配的内存量,这与导致错误的数字相同。

    memory_get_usage 将返回调用时分配的活动内存量。

    ini_set('memory_limit', '256M'); 将设置 PHP 占用系统内存的最大允许值。如果您在 768K 时获得 OOM,则提高它不会解决问题。

    没有迹象表明您使用的是什么版本的 PHP,但我建议立即升级。 Zend 的内存管理器有几个错误无法释放内存,这将导致您完全相同的问题。

    您的本地服务器和生产服务器是否都运行相同版本的操作系统、相同的长位和相同版本的 PHP?答案是否定的。

    如果它与 windows malloc() 问题无关,因为它是子域并且可能在 VirtualHost 中,并且仅分配 768k,这听起来几乎是操作系统问题。

    访问脚本时从命令提示符运行tasklist。您是否看到额外的 Apache 线程,或跨进程的内存使用量激增?

    最后一个想法是,在表格行/列的每个循环之后运行flush() 和/或ob_flush();。这应该会清除您的缓冲区并在出现问题时为您节省一些内存。

    【讨论】:

    • 如果您的文件是 Zend 编码的 - 这可能是问题所在。 Zend 编码文件不会像应有的那样释放内存(即使您取消设置变量)。我的脚本在大文件上总是会耗尽内存 - 但前提是脚本是 Zend 编码的(使用未编码的版本每次都能完美运行)
    • 将 ZendOpcache 添加到 PHP 5.4.13 w/php-fpm 和 fcgi 导致我在选择页面上出现内存限制错误。感谢 Zend 的提示。
    【解决方案6】:

    请注意,错误是Out of memory,而不是Allowed memory size [..] exhausted

    所以内存泄漏在系统的其他地方。在这个繁重的查询之后,mysql 服务器可能会使用大量的系统内存,从而使 apache/php 没有物理和交换。

    这应该总是在同一行(和/或在同一脚本中)解释错误。

    【讨论】:

    • 在我的情况下 mysql 内存不足 - 它在单独的主机上运行。
    【解决方案7】:

    我在尝试使用交换时遇到了与服务器死机相同的问题。这是因为 mod_php 永远不会释放内存。因此,Apache 进程不断增长,要么达到 apache 或 PHP 的内存限制,要么,如果没有限制,则使服务器崩溃。

    重新启动 apache 会使其产生新的苗条进程,但随着时间的推移它们会运行 PHP 脚本,它们会不断增长,直到出现问题。

    解决方案是让 apache 在提供一定数量的查询后杀死进程,以便创建新的进程(有 some questions related to that)将 MaxRequestsPerChild 配置选项减少到,假设为 100(默认为 1000)。

    当然,这可能会降低服务器性能,因为它需要资源来杀死和生成新进程,但至少它可以使站点保持正常运行。您可能很想提高正在运行的进程数以保持高性能,请确保 PHP(或 apache)内存限制 x 最大进程数不会超过服务器的物理内存。

    这是我的经验,希望对您有所帮助。

    【讨论】:

    • 我有这个错误,我现在正在使用 xampp..如果我使用服务器,我还会遇到这种问题吗?从你的回答我认为我得到错误的原因是我的 ram 没有处理除非有更高内存的服务器会处理这个问题吗?如果我错了,请纠正我
    • MaxRequestsPerChild 10000 默认为 10000 它实际上看起来像 httpd.apache.org/docs/2.2/mod/…
    【解决方案8】:

    可能是 MySQL 和打开的连接数的问题,因此当您每隔几天重新启动时它会自行解决。它们会在脚本关闭时自动关闭吗?

    【讨论】:

    • 我只重启 Apache 来解决问题。该脚本不经常调用 MySQL,因为它们大多使用 memcached 缓存。
    【解决方案9】:

    我猜你要么没有编辑正确的php.ini,要么没有重新启动 PHP 和/或网络服务器。

    在您的文档根目录中创建一个phpinfo.php 页面,其中包含<?php phpinfo(); 的内容,以确保您更改的是正确的php.ini。除了网络服务器正在使用的php.ini 文件的位置之外,它还会说明允许的最大脚本内存。

    接下来,我将向您的页面添加一些堆栈跟踪,以便您查看导致此的事件链。以下函数将捕获致命错误并提供有关所发生情况的更多信息。

    register_shutdown_function(function()
    {
        if($error = error_get_last())
        {
            // Should actually log this instead of printing out...
            var_dump($error);
            var_dump(debug_backtrace());
        }
    });
    

    就我个人而言,Nginx + PHP-FPM 是我离开慢速 Apache 后多年来一直使用的。

    【讨论】:

    • 首先,我完全知道我需要重新启动 Apache 才能使我的 PHP 设置生效。我已经包含了 xDebug 生成的 cachegrind.out。我的网站是一个高流量网站,平均每分钟有 1,000 名访问者。
    • @invisal,我不怀疑你是一个非常有能力的开发人员 - 但有时简单的事情会被忽略。你确认你编辑的是正确的php.ini吗?
    • 我对 phpinfo() 进行了仔细检查。我现在得到了正确的设置。我现在正在努力减少内存使用。 (希望能解决问题)
    【解决方案10】:

    这几天前发生在我身上。我做了一个全新的安装,它仍然发生。据每个人都看到并基于您的服务器规格。很可能它是一个无限循环。它可能不在 PHP 代码本身上,而是在向 Apache 发出的请求上。

    让我们说当你访问这个 url http://localhost/mysite/page_with_multiple_requests

    如果收到多个请求,请检查 Apache 的访问日志。跟踪该请求并检查可能导致系统“瓶颈”的代码(使用 sendmail 时我的 exec())。我所说的瓶颈不需要是“无限循环”。它可能是一个需要一些时间才能完成的功能。或者可能是一些 php 的 'program execution functions'

    您可能还需要检查 ajax 请求(在页面加载时执行的请求)。如果该 ajax 请求重定向到相同的 url

    例如httpx://localhost/mysite/page_with_multiple_requests

    它会重新“重做”请求

    如果您在脚本结束的地方发布随机行或代码本身可能会有所帮助,那里可能有一个“循环”代码。恕我直言 php 不会白白调用随机行。

    http://blog.piratelufi.com/2012/08/browser-sending-multiple-requests-at-once/

    【讨论】:

      【解决方案11】:

      只是回顾一下(我将这个答案添加到与原始问题相距甚远的地方):

      • PHP 无法分配看似少量的内存
      • 错误发生时的当前内存使用量 + 请求的数量小于当前有效的内存限制
      • 发生这种情况时,系统有 6Gb 可供 PHP 使用
      • 由于问题通过重新启动 apache 得到解决 - 是 apache 阻止了 PHP 可用的内存

      如果这些都是有效的,那么唯一可能的解释是 6Gb 非常 碎片化 - 我认为这不太可能。您没有说如何从 Apache 调用 PHP - mod_php?每分钟? Fcgi?

      我将首先检查上述每个谓词——尤其是空闲内存谓词。你怎么知道有 6Gb 空闲 当错误发生时?更可能的原因是发生了您没有发现的内存泄漏。

      您没有提供有关如何配置 apache 的任何详细信息;我还想看看减少 MaxRequestsPerChild 和 MaxMemFree。 (我对每个线程都适用的worker apache不是很熟悉——实际上你需要对每个进程进行限制)。如果您从 apache 配置中提供了核心设置,那么也许我们可以提出进一步的建议。

      除非您广泛使用 Ajax,否则请确保您的 keepalive 时间为 2 或更少。

      【讨论】:

        【解决方案12】:

        嘿,我的服务器上也遇到了同样的问题。我只是更改了以下内容:

        php.ini 更改为...

        memory_limit = 128M
        

        并添加到httpd.conf

        RLimitMEM 1073741824 2147483648
        

        并重新启动 apache & 我删除了错误:

        【讨论】:

          【解决方案13】:

          大多数时候你会遇到这样的错误,问题出在代码中。我并不是说您编写的代码很糟糕,我想说的是您需要仔细观察其中使用了这么多内存。

          永远记住“PHP 中的垃圾收集非常糟糕”,它不像 Java,任何其他这样的语言。有一种方法可以通过 gc_collect_cycle 强制进行垃圾收集,但在我个人看来,这并不能解决您的问题。 一旦请求-响应周期完成,PHP 会释放用于执行页面的所有内存,因此如果您的脚本长时间运行,例如后台脚本(Gearman 等),您可能会遇到内存问题,因为直到内存才会被释放脚本正在运行。

          如果您的 scr,pt 不是上述情况,并且正如您所说,没有代码需要如此大量的内存,那么问题肯定出在代码本身,升级到任何版本的 PHP 都赢了解决不了问题。我曾经遇到过我的一个 Gearman 脚本,我的一个循环出现了问题,我将一个变量附加到我的数组之一,变量本身非常重(大约 110KB 的数据)。所以我建议,仔细检查你的代码。

          狂喜

          【讨论】:

            【解决方案14】:

            我在使用 PHP 时遇到了类似的问题:

            1) 检查您的错误日志。在继续之前消除每个错误。 2) 考虑修改您的 apache 配置以消除未使用的模块 - 这将减少 PHP 所需的占用空间 - 这是一个很好的链接 - 它特定于 Wordpress,但仍然非常有用http://thethemefoundry.com/blog/optimize-apache-wordpress/

            为了让您了解我发现的那种错误,我有一些代码试图将内容发布到 Facebook,然后 Facebook 修改了他们的 API,所以这坏了,我还使用了“内容过期器”,这基本上意味着它不断重试将此内容发布到 Facebook,并在内存中留下大量对象。

            【讨论】:

            • 感谢您的指出。我正在调整我的 Apache 配置。我需要几天时间来监控每个更改的配置,以确保它完美无缺。谢谢。
            【解决方案15】:

            从探查器输出文件中,我注意到一些我不太喜欢/不太信任的事情,我会研究这些:

            除了不知道输出数字的含义来检测异常或 PHP 脚本如何工作......,这不是问题吗?对同一个 main.ph 文件有一个包含,这看起来像一个递归的东西?

            2121 fl=D:\www\football\views\main.php
            2122 fn=include::D:\www\football\views\main.php
            

            注意到文件D:\www\football\views\main.php 多次使用一些字符串函数,我猜它是在查询返回的数据上调用这些函数:

            strlen
            substr
            strtotime
            

            如果像在 C 语言中一样,这些函数要求字符串以 null 终止或字符串终止符的其他结尾以避免内存问题,我会查看您的查询返回的字符串。

            你能发布你网站的网址吗?

            【讨论】:

            • football.khmerload.com 我从您之前对问题的编辑中获得了网址。
            • 我多次使用 strlen、substr 和 strtotime,因为我需要将英文日期转换为我的语言日期。 views\main.php 仅包含一次。
            【解决方案16】:

            这是 PHP v 5.2 for Windows 中的一个已知错误,它至少存在于 5.2.3 版中:https://bugs.php.net/bug.php?id=41615

            建议的修复对我们都没有帮助,我们将不得不更新 PHP。

            【讨论】:

              【解决方案17】:

              尝试通过 fcgid 运行 php,这可能会有所帮助:

              这些是您在运行 PHP 时会看到的经典错误 阿帕奇模块。我们与这些错误斗争了几个月。切换到 通过 mod_fcgid 使用 PHP(正如 James 建议的那样)将解决所有这些问题 问题。确保您拥有最新的 Visual C++ Redistributable 安装包:

              http://support.microsoft.com/kb/2019667

              另外,我建议切换到 64 位版本的 MySQL。没有真正的 不再运行 32 位版本的理由。

              来源:Apache 2.4.6.0 crash due to a problem in php5ts.dll 5.5.1.0

              【讨论】:

                【解决方案18】:

                就我而言,这个错误是由于一个巨大的选择查询(数十万个返回结果)而触发的。

                在我的数据库中添加数百万条记录以测试 WordPress 的可扩展性后,它立即出现了,所以这对我来说是唯一可能的原因。

                【讨论】:

                  【解决方案19】:

                  致命错误:内存不足(已分配已解决
                  我有类似的问题,几个月没有解决方案。最后,我检查了其中一个 apache 文件夹,即(\apache\conf\extra),我遇到了这个控制 apache 内存分配的文件。文件名是 httpd-mpm 在该文件中,您要将 MaxMemFree 设置为 2048 更高,我将第一个 MaxMemFree (IfModule !mpm_netware_module) 设置为 10000 然后制作第二个MaxMemFree 到 5000 IfModule mpm_netware_module。

                  这些解决了我的问题。希望对你有帮助

                  【讨论】:

                    【解决方案20】:

                    我知道这是一个旧线程,但这是我解决它的经验。

                    我的服务器是运行 Apache 的托管服务。

                    我的脚本在 6Mb 内存不足时崩溃了,而我的限制是 256Mb - 疯了,是吗?

                    它通过 http 回调同步调用,从我的客户端上运行的 javascript 调用,并在大约 550 次调用后崩溃。 在与不称职的“升级支持”人员浪费了很多时间之后,我的脚本现在神奇地运行了。

                    他们说他们所做的只是重置 php.ini,但我检查了差异:

                    我看不到可能与内存不足错误有关的任何更改。

                    我怀疑我的“升级支持”人员以重置 php.ini 为幌子隐藏在 Web 服务器中的内存泄漏。而且,真的,我不是阴谋论者。

                    【讨论】:

                      猜你喜欢
                      • 2014-06-01
                      • 1970-01-01
                      • 1970-01-01
                      • 2014-01-25
                      • 2019-03-18
                      • 1970-01-01
                      • 2020-12-15
                      • 2011-10-16
                      相关资源
                      最近更新 更多