如何找到哪个PHP脚本泄漏内存?

时间:2013-04-17 19:15:20

标签: php memory-management memory-leaks

我的专用服务器有32GB RAM,内存不断上升,我现在必须每天重新启动它。这给我的客户和金钱带来了损失。

我很难找到内存泄漏的位置。我在网上找到的只是人们说“使用xdebug”,但我找不到任何关于查找内存泄漏的xdebug教程。我已经尝试在函数调用之前和之后打印memory_get_usage但这是正确的方法吗?

我运行了很多php脚本 - 一些来自访问者,另一些来自cron作业 - 我需要找到哪一个正在泄漏内存并尽快修复它但我甚至不知道如何确定是否一个给定的功能是否泄漏记忆。

我已经尝试在函数调用之前和之后打印memory_get_usage,然后它会上升,但是如果我不止一次调用该函数,它就不再上升了。有人可以解释一下,并告诉我如何简单轻松地判断PHP函数是否有内存泄漏?

4 个答案:

答案 0 :(得分:21)

你可以做各种各样的事情,但首先你应该尽量避免创建内存泄漏。

让我澄清一下:PHP是一种脚本语言,它不是为长时间运行的脚本而设计的,因此它的内存管理并不是市场上最好的。但为什么会这样呢?它的目的是在请求级别上调用,因此其运行范围非常小(不超过2-3秒)。其他一切都应该放在后台。

我可以对内存泄漏做些什么?

  1. 如果您的版本低于5.4,则需要处理圈子引用,因为这些不是垃圾回收。

  2. 如果您需要连续运行脚本,您可能会考虑采用不同的方法。尝试while(true)实现,但在脚本周围包装supervisorhttp://supervisord.org),并在结束后调用它。这样你100%确定你永远不会泄漏内存。

  3. 您可以使用xdebug逐个分析您的脚本,并找出消耗大量内存的地方。

  4. 如果该类不再需要,您可以实现析构函数来取消设置所有引用。

    public function __destruct(){
        $this->cleanup();
    }
    
    public function cleanup() {
        //cleanup everything from attributes
        foreach (get_class_vars(__CLASS__) as $clsVar => $_) {
            unset($this->$clsVar);
        }
    
        //cleanup all objects inside data array
        if (is_array($this->_data)) {
            foreach ($this->_data as $value) {
                if (is_object($value) && method_exists($value, 'cleanUp')) {
                    $value->cleanUp();
                }
            }
        }
    }
    
  5. 阅读有关垃圾回收的PHP文档http://us3.php.net/manual/en/features.gc.php

  6. 避免全局变量,因为它们从不被垃圾收集,需要明确地unset。如果您使用的是ZF或Symfony这样的框架可能无法实现,那么如果您这样做会破坏功能。

  7. 最后但并非最不重要的是我想再次强调,PHP不适合长时间运行的脚本!如果你有事情要做,那就需要连续运行你不应该因为PHP中的内存泄漏而崩溃,而是花时间学习更复杂的语言,比如JAVA或C#。

答案 1 :(得分:6)

看看这个php-extension:https://github.com/arnaud-lb/php-memory-profiler。 您可以使用不同格式转储信息,并通过以下工具进行简单分析,例如:Google Performance ToolsKCacheGrindQCacheGrind

答案 2 :(得分:5)

我找到了适合我的方法:

  1. 安装“php-memprof”扩展名。你可以在Ubuntu上运行:

    sudo pecl install memprof

  2. 安装“google-perftools”。再次为Ubuntu:

    sudo apt-get install google-perftools

  3. 添加此代码以开始您的脚本:

    if (function_exists('memprof_enable')) {
        memprof_enable();
    }
    
  4. 这个令人感到困惑的地方是你找不到内存泄漏:

    if (function_exists("memprof_dump_pprof"))
    {
        $time = microtime(true);
        $f = fopen("/tmp/profile_$time.heap", "w");
        memprof_dump_pprof($f);
        fclose($f);
        echo "Memory profile dumped. ";
    }
    

    在我的情况下,它每100次运行就在大循环内。

  5. 运行google-pprof比较2个内存转储:

    google-pprof --web --base=/tmp/profile_17.heap /tmp/profile_18.heap
    

    这将在您的浏览器中打开这样的svg图像:

    sample from doc

    您可以在gperftools documentation

  6. 中找到数字和名称的说明

    P.S。修复php级别的泄漏并不能保证解释器中没有内存泄漏。在我的情况下,我最终只是在较长时间内重新启动sctipt。

答案 3 :(得分:1)

我不是内存使用专家,但也许这种方法可以帮助您检测有问题的脚本:

获取信息: 1.使用apache访问日志文件 2.创建自己的内存使用日志文件(http://www.webhostingtalk.com/showthread.php?t=617742

检查内存使用率上升的时间,并与apache访问日志进行比较。

它至少会向您提供有关使用率是缓慢且持续增长还是从某个点开始使用的信息。

祝你好运!

相关问题