你如何调试php“Out of Memory”问题?

时间:2011-05-24 17:07:46

标签: php debugging memory methodology

我最近遇到了一些关于PHP内存限制的问题:

  

内存不足(分配22544384)(试图分配232个字节)

这些都是调试的麻烦,因为我没有留下关于导致问题的原因的大量信息。

添加关机功能有帮助

register_shutdown_function('shutdown');

然后,使用error_get_last();我可以获取有关上一个错误的信息,在这种情况下,“内存不足”致命错误,例如行号和php文件名。

这很好,但是我的php程序非常面向对象。堆栈深处的错误并没有告诉我有关错误时控制结构或执行堆栈的信息。我已经尝试了debug_backtrace(),,但这只是在关机期间向我显示堆栈,而不是错误发生时的堆栈。

我知道我可以使用ini_set或修改php.ini来提高内存限制,但这并没有让我更接近实际找出消耗如此多内存的内容或错误期间执行流程的样子。

任何人都有一个很好的方法来调试高级面向对象的PHP程序中的内存错误吗?

6 个答案:

答案 0 :(得分:11)

echo '<pre>';
$vars = get_defined_vars();
foreach($vars as $name=>$var)
{
    echo '<strong>' . $name . '</strong>: ' . strlen(serialize($var)) . '<br />';
}
exit();

/* ... Code that triggers memory error ... */

我使用它在我的代码的问题部分之前打印出当前分配的变量列表,以及对变量大小的(非常)粗略估计。我回去并unset任何在兴趣点之外都不需要的东西。

安装扩展程序不是一个选项时很有用。

你可以修改上面的代码,使用memory_get_usage的方式可以对变量中的内存进行不同的估计,不确定它是好还是坏。

答案 1 :(得分:7)

Memprof是一个php扩展,可​​帮助查找那些内存消息片段,特别是面向对象的代码。

This adapted tutorial非常有用。

注意:我没有成功尝试为Windows编译此扩展。如果您这样做,请确保您的PHP不是线程安全的。为了避免一些麻烦,我建议你在* nix环境下使用它。

另一个有趣的链接是描述php如何处理内存的slideshare。它为您提供了有关脚本内存使用情况的一些线索。

答案 2 :(得分:6)

我想知道也许你的思考方法在这里存在缺陷。

您问题的基本答案 - 如何找出此错误发生的位置? - 已经回答;你知道是什么造成的。

然而,这是触发错误实际上不是问题的情况之一 - 当然,232字节对象根本不是你的问题。这是在它之前分配的20 + Megs。

已经发布了一些可以帮助您追踪这一点的想法;你真的需要在应用程序架构中看到“更高级别”,而不仅仅是单个函数。

可能是您的应用程序需要更多内存来执行它所做的事情,并且您拥有用户负载。或者可能有一些不必要的真实内存生猪 - 但你必须知道回答这个问题的必要性。

这基本上意味着逐行逐行,逐个对象,根据需要进行分析,直到找到你想要的东西;大内存用户。请注意,可能没有一个或两个大项目......如果只是它那么容易!找到内存后,你必须弄清楚它们是否可以优化。如果没有,那么你需要更多的记忆。

答案 3 :(得分:3)

检查函数memory_get_usage()的文档以查看运行时的内存使用情况。

答案 4 :(得分:3)

网站“ IF!1 0 ”提供了一个简单易用的MemoryUsageInformation课程。它对于调试内存泄漏非常有用。

<?php

class MemoryUsageInformation
{

    private $real_usage;
    private $statistics = array();

    // Memory Usage Information constructor
    public function __construct($real_usage = false)
    {
        $this->real_usage = $real_usage;
    }

    // Returns current memory usage with or without styling
    public function getCurrentMemoryUsage($with_style = true)
    {
        $mem = memory_get_usage($this->real_usage);
        return ($with_style) ? $this->byteFormat($mem) : $mem;
    }

    // Returns peak of memory usage
    public function getPeakMemoryUsage($with_style = true)
    {
        $mem = memory_get_peak_usage($this->real_usage);
        return ($with_style) ? $this->byteFormat($mem) : $mem;
    }

    // Set memory usage with info
    public function setMemoryUsage($info = '')
    {
        $this->statistics[] = array('time' => time(),
            'info' => $info,
            'memory_usage' => $this->getCurrentMemoryUsage());
    }

    // Print all memory usage info and memory limit and 
    public function printMemoryUsageInformation()
    {
        foreach ($this->statistics as $satistic)
        {
            echo "Time: " . $satistic['time'] .
            " | Memory Usage: " . $satistic['memory_usage'] .
            " | Info: " . $satistic['info'];
            echo "\n";
        }
        echo "\n\n";
        echo "Peak of memory usage: " . $this->getPeakMemoryUsage();
        echo "\n\n";
    }

    // Set start with default info or some custom info
    public function setStart($info = 'Initial Memory Usage')
    {
        $this->setMemoryUsage($info);
    }

    // Set end with default info or some custom info
    public function setEnd($info = 'Memory Usage at the End')
    {
        $this->setMemoryUsage($info);
    }

    // Byte formatting
    private function byteFormat($bytes, $unit = "", $decimals = 2)
    {
        $units = array('B' => 0, 'KB' => 1, 'MB' => 2, 'GB' => 3, 'TB' => 4,
            'PB' => 5, 'EB' => 6, 'ZB' => 7, 'YB' => 8);

        $value = 0;
        if ($bytes > 0)
        {
            // Generate automatic prefix by bytes 
            // If wrong prefix given
            if (!array_key_exists($unit, $units))
            {
                $pow = floor(log($bytes) / log(1024));
                $unit = array_search($pow, $units);
            }

            // Calculate byte value by prefix
            $value = ($bytes / pow(1024, floor($units[$unit])));
        }

        // If decimals is not numeric or decimals is less than 0 
        // then set default value
        if (!is_numeric($decimals) || $decimals < 0)
        {
            $decimals = 2;
        }

        // Format output
        return sprintf('%.' . $decimals . 'f ' . $unit, $value);
    }

}

答案 5 :(得分:0)

使用xdebug来分析内存使用情况。