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

Guy*_*Guy 30 php memory-leaks memory-management

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

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

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

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

Mat*_*aug 23

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

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

我可以做些什么来防止内存泄漏?

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

  2. 如果您需要连续运行脚本,您可能会考虑采用不同的方法.尝试while(true)实现,但在脚本周围包装supervisor(http://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();
                }
            }
        }
    }
    
    Run Code Online (Sandbox Code Playgroud)
  5. 阅读有关垃圾收集的PHP文档http://us3.php.net/manual/en/features.gc.php

  6. 避免使用全局变量,因为它们永远不会被垃圾收集,需要unset明确.如果你使用ZF或Symfony之类的框架可能是不可能的,那么如果你这样做会破坏功能.

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

  • 我不同意 PHP 不是一门伟大的语言。我从 1981 年就开始编码。我可以用 Java、C# 等编码。大多数人对 PHP 存在偏见。Facebook 是 PHP(重新命名?)!Java在学术界得到推广。即使 BASIC 也是很棒的语言。每种语言都有其优点和缺点。Java 的开发开销 ($$$) 比 PHP 高出 30% 到 60%!尝试使用 Z-80 进行编码。80x86、MX6800x 等...这完全取决于您是一个多么优秀的程序员(工程师)! (4认同)

Sla*_*lam 6

看看这个php扩展:https://github.com/arnaud-lb/php-memory-profiler.您可以转储不同格式的信息,并通过一些工具进行简单分析,例如:Google Performance Tools,KCacheGrindQCacheGrind.


小智 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();
    }
    
    Run Code Online (Sandbox Code Playgroud)
  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. ";
    }
    
    Run Code Online (Sandbox Code Playgroud)

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

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

    google-pprof --web --base=/tmp/profile_17.heap /tmp/profile_18.heap
    
    Run Code Online (Sandbox Code Playgroud)

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

    来自doc的样本

    您可以在gperftools文档中找到数字和名称的描述

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