如何调试php;内存不足”;问题


How do you debug php "Out of Memory" issues?

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

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

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

添加关闭功能有助于

register_shutdown_function('shutdown');

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

这很好,但我的php程序是高度面向对象的。堆栈中的错误在发生错误时并不能告诉我太多关于控制结构或执行堆栈的信息。我尝试过debug_backtrace(),但它只是显示关闭期间的堆栈,而不是错误发生时的堆栈。

我知道我可以使用ini_set或修改php.ini来提高内存限制,但这并不能让我更接近于真正弄清楚是什么消耗了这么多内存,或者在错误期间我的执行流是什么样子的。

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

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,这样可以对变量中的内存进行不同的估计,而不确定它是好是坏。

Memprof是一个php扩展,它有助于查找那些消耗内存的片段,特别是在面向对象的代码中。

这个经过改编的教程非常有用。

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

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

我想知道你的思维方式在这里是否有缺陷。

你的问题的基本答案-我如何找出这个错误发生的地方?-已经得到答复;你知道是什么原因造成的。

然而,在这种情况下,触发错误并不是真正的问题——当然,232字节的对象根本不是你的问题。这是之前分配的20多兆欧。

已经发布了一些想法,可以帮助你追踪这些想法;在这里,您确实需要从"更高的层次"看应用程序体系结构,而不仅仅是单个功能。

这可能是因为您的应用程序需要更多的内存来完成它所做的事情,而您所拥有的用户负载。或者可能有一些真正的内存占用是不必要的,但你必须知道什么是必要的或不需要回答这个问题。

这基本上意味着一行一行,一个对象一个对象,根据需要进行分析,直到你找到你想要的;大内存用户。请注意,可能没有一两个大项目。。。要是这么容易就好了!一旦你发现内存占用,你就必须弄清楚它们是否可以优化。如果没有,那么你需要更多的内存。

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

网站"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);
    }
}

使用xdebug来评测内存使用情况。