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

时间:2022-09-06 14:04:32

I've had some issues lately with PHP memory limits lately:

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

Out of memory (allocated 22544384) (tried to allocate 232 bytes)

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

These are quite the nuisance to debug since I'm not left with a lot of info about what caused the issue.

这些是非常讨厌的调试,因为我没有留下太多关于什么导致这个问题的信息。

Adding a shutdown function has helped

添加关机功能也有帮助

register_shutdown_function('shutdown');

then, using error_get_last(); I can obtain information about the last error, in this case, the "Out of memory" fatal error, such as the line number, and the php file name.

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

This is nice and all, but my php program is heavily object oriented. An error deep in the stack doesn't tell me much about the control structure or the execution stack at the moment of the error. I've tried debug_backtrace(), but that just shows me the stack during shutdown, not the stack at the time of the error.

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

I know I can just raise the memory limit using ini_set or modifying php.ini, but that doesn't get me any closer to actually figuring out what is consuming so much memory or what my execution flow looks like during the error.

我知道我可以使用ini_set或修改php来提高内存限制。ini,但是这并不能让我更接近实际地计算出什么消耗了这么多内存或者我的执行流在错误期间是什么样子。

Anyone have a good methodology for debugging memory errors in advanced Object Oriented PHP programs?

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

6 个解决方案

#1


10  

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 ... */

I use this to print out a list of currently assigned variables just before a problem section of my code, along with a (very) rough estimate of the size of the variable. I go back and unset anything that isn't needed at and beyond the point of interest.

我使用它在代码的问题部分之前打印出当前分配的变量列表,以及对变量大小的粗略估计。我返回并取消在兴趣点之外不需要的任何设置。

It's useful when installing an extension isn't an option.

当安装扩展不是一个选项时,它是有用的。

You could modify the above code to use memory_get_usage in a way that will give you a different estimate of the memory in a variable, not sure whether it'd be better or worse.

您可以修改上面的代码来使用memory_get_usage,其方式将使您对变量中的内存有不同的估计,但不确定它是好是坏。

#2


7  

Memprof is a php extension that helps finding those memory-eaters snippets, specially in object-oriented codes.

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

This adapted tutorial is quite useful.

这个改编教程非常有用。

Note: I unsuccessfully tried to compile this extension for windows. If you try so, be sure your php is not thread safe. To avoid some headaches I suggest you to use it under *nix environments.

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

Another interesting link was a slideshare describing how php handles memory. It gives you some clues about your script's memory usage.

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

#3


6  

I wonder is perhaps your thinking regards methodology is flawed here.

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

The basic answer to your question - how do I find out where this error is occurring? - has already been answered; you know what's causing that.

你的问题的基本答案是——我如何找出错误发生的地方?-已得到答复;你知道是什么引起的。

However, this is one of those cases where the triggering error isn't really the problem - certainly, that 232 byte object isn't your problem at all. It is the 20+Megs that was allocated before it.

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

There have been some ideas posted which can help you track that down; you really need to look "higher level" here, at the application architecture, and not just at individual functions.

有一些想法可以帮助你找到它;您确实需要在这里查看“更高级别”,在应用程序体系结构中,而不仅仅是在单个函数中。

It may be that your application requires more memory to do what it does, with the user load you have. Or it may be that there are some real memory hogs that are unnecessary - but you have to know what is necessary or not to answer that question.

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

That basically means going line-by-line, object-by-object, profiling as needed, until you find what you seek; big memory users. Note that there might not be one or two big items... if only it were so easy! Once you find the memory-hogs, you then have to figure out if they can be optimized. If not, then you need more memory.

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

#4


3  

Check the documentation of the function memory_get_usage() to view the memory usage in run time.

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

#5


3  

Website "IF !1 0" provides a simple to use MemoryUsageInformation class. It is very useful for debugging memory leaks.

“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);
    }

}

#6


0  

Use xdebug to profile memory usage.

使用xdebug配置内存使用。

#1


10  

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 ... */

I use this to print out a list of currently assigned variables just before a problem section of my code, along with a (very) rough estimate of the size of the variable. I go back and unset anything that isn't needed at and beyond the point of interest.

我使用它在代码的问题部分之前打印出当前分配的变量列表,以及对变量大小的粗略估计。我返回并取消在兴趣点之外不需要的任何设置。

It's useful when installing an extension isn't an option.

当安装扩展不是一个选项时,它是有用的。

You could modify the above code to use memory_get_usage in a way that will give you a different estimate of the memory in a variable, not sure whether it'd be better or worse.

您可以修改上面的代码来使用memory_get_usage,其方式将使您对变量中的内存有不同的估计,但不确定它是好是坏。

#2


7  

Memprof is a php extension that helps finding those memory-eaters snippets, specially in object-oriented codes.

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

This adapted tutorial is quite useful.

这个改编教程非常有用。

Note: I unsuccessfully tried to compile this extension for windows. If you try so, be sure your php is not thread safe. To avoid some headaches I suggest you to use it under *nix environments.

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

Another interesting link was a slideshare describing how php handles memory. It gives you some clues about your script's memory usage.

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

#3


6  

I wonder is perhaps your thinking regards methodology is flawed here.

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

The basic answer to your question - how do I find out where this error is occurring? - has already been answered; you know what's causing that.

你的问题的基本答案是——我如何找出错误发生的地方?-已得到答复;你知道是什么引起的。

However, this is one of those cases where the triggering error isn't really the problem - certainly, that 232 byte object isn't your problem at all. It is the 20+Megs that was allocated before it.

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

There have been some ideas posted which can help you track that down; you really need to look "higher level" here, at the application architecture, and not just at individual functions.

有一些想法可以帮助你找到它;您确实需要在这里查看“更高级别”,在应用程序体系结构中,而不仅仅是在单个函数中。

It may be that your application requires more memory to do what it does, with the user load you have. Or it may be that there are some real memory hogs that are unnecessary - but you have to know what is necessary or not to answer that question.

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

That basically means going line-by-line, object-by-object, profiling as needed, until you find what you seek; big memory users. Note that there might not be one or two big items... if only it were so easy! Once you find the memory-hogs, you then have to figure out if they can be optimized. If not, then you need more memory.

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

#4


3  

Check the documentation of the function memory_get_usage() to view the memory usage in run time.

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

#5


3  

Website "IF !1 0" provides a simple to use MemoryUsageInformation class. It is very useful for debugging memory leaks.

“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);
    }

}

#6


0  

Use xdebug to profile memory usage.

使用xdebug配置内存使用。