我有一堆客户端销售点(POS)系统,这些系统定期将新的销售数据发送到一个集中的数据库,该数据库将数据存储到一个大数据库中,以便生成报告。

客户机POS基于PHPPOS,我实现了一个模块,该模块使用标准XML-RPC库将销售数据发送到服务。服务器系统构建在CodeIgniter上,并为webservice组件使用XML-RPC和XML-RPC库。每当我发送大量的销售数据(从销售表中只有50行,从sales_items中单独的行用于销售中的每个项目),我得到以下错误:

Fatal error: Allowed memory size of 134217728 bytes exhausted (tried to allocate 54 bytes)

128M是php.ini中的默认值,但我认为这是一个巨大的数字。事实上,我甚至尝试过将这个值设置为1024M,它所做的只是花费更长的时间来出错。

至于我所采取的步骤,我已经尝试在服务器端禁用所有处理,并对其进行了修改,使其返回一个罐装响应,而不管输入是什么。但是,我认为问题出在数据的实际发送上。我甚至尝试过禁用PHP的最大脚本执行时间,但它仍然出错。


当前回答

PHP的内存分配可以永久调整,也可以临时调整。

永久

可以通过两种方式永久地更改PHP内存分配。

如果您可以访问php.ini文件,您可以将memory_limit的值编辑为您想要的值。

如果你不能访问你的php.ini文件(你的主机允许),你可以通过你的.htaccess文件覆盖内存分配。添加php_value memory_limit 128M(或您想要的分配值)。

临时

您可以在PHP文件中动态地调整内存分配。你只需有代码ini_set('memory_limit', '128M');(或者你想要的分配)。您可以通过将值设置为“-1”来删除内存限制(尽管机器或实例限制仍然可能应用)。

其他回答

对于那些正在挠头寻找为什么这个小函数会导致内存泄漏的人来说,有时由于一个小错误,一个函数开始递归地永远调用自己。

例如,代理类与将要代理它的对象的函数具有相同的名称。

class Proxy {

    private $actualObject;

    public function doSomething() {

        return $this->actualObjec->doSomething();
    }
}

有时你可能会忘记带那个小的actualobjecc成员,因为代理实际上有那个doSomething方法,PHP不会给你任何错误,对于一个大的类,它可能会被隐藏几分钟,以找出它泄漏内存的原因。

只需添加一个ini_set('memory_limit', '-1');在你的网页顶部的行。

并且您可以根据需要将内存设置在-1的位置,设置为16M,等等。

我在一个比以前工作的数据集更小的数据集上运行时出现了下面的错误。

致命错误:在C:\workspace\image_management.php的第173行中,允许的内存大小为134217728字节已耗尽(试图分配4096字节)

由于寻找故障把我带到了这里,我想我应该提到,在以前的回答中,它并不总是技术解决方案,而是一些更简单的东西。对我来说,是火狐。在我运行程序之前,它已经使用了1157 MB。

事实证明,我在一段时间内每次都在看一个50分钟的视频,这把事情搞砸了。这是一种专家们想都不用想就能纠正的修复方法,但对我这样的人来说,这是值得记住的。

在PHP脚本中很容易发生内存泄漏——特别是在使用抽象(如ORM)时。尝试使用Xdebug分析脚本并找出所有内存的去向。

对我来说,这个错误消息最常见的原因是PHP for语句中遗漏了“++”操作符。这将导致循环永远持续下去,无论您允许使用多少内存。这是一个简单的语法错误,但是编译器或运行时系统很难检测到。我们很容易纠正,如果我们想去寻找它!

但是假设您想要一个通用的过程来尽早停止这样的循环并报告错误?您可以像下面讨论的那样简单地检测每个循环(或者至少是最里面的循环)。

在某些情况下,例如异常内部的递归,set_time_limit会失败,浏览器会继续尝试加载PHP输出,要么是无限循环,要么是致命的错误消息,这就是这个问题的主题。

通过减少代码开头附近允许的分配大小,您可能能够防止致命错误,正如在其他答案中讨论的那样。

然后,您可能会得到一个终止的程序,但仍然难以调试。

无论您的程序是否终止,通过在程序中插入BreakLoop()调用来检测代码,以获得控制权,并找出程序中的哪个循环或递归导致了问题。

BreakLoop的定义如下:

function BreakLoop($MaxRepetitions=500,$LoopSite="unspecified")
    {
    static $Sites=[];
    if (!@$Sites[$LoopSite] || !$MaxRepetitions)
        $Sites[$LoopSite]=['n'=>0, 'if'=>0];
    if (!$MaxRepetitions)
        return;
    if (++$Sites[$LoopSite]['n'] >= $MaxRepetitions)
        {
        $S=debug_backtrace(); // array_reverse
        $info=$S[0];
        $File=$info['file'];
        $Line=$info['line'];
        exit("*** Loop for site $LoopSite was interrupted after $MaxRepetitions repetitions. In file $File at line $Line.");
        }
    } // BreakLoop

$LoopSite参数可以是代码中函数的名称。实际上没有必要,因为您将得到的错误消息将指向包含BreakLoop()调用的行。