如何查找哪个 PHP 脚本正在泄漏内存?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/16068301/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-25 10:28:49  来源:igfitidea点击:

How to find which PHP script is leaking memory?

phpmemory-managementmemory-leaks

提问by Guy

My dedicated server has 32GB RAM and the memory just goes up and up constantly and I have to reboot it daily now. This is costing me customers and money.

我的专用服务器有 32GB 内存,内存不断增加,我现在必须每天重新启动它。这让我失去了客户和金钱。

I am having a hard time finding where the memory leak is. All I can find online is people say "Use xdebug" but I haven't been able to find any xdebug tutorials on finding memory leaks. I have tried printing memory_get_usage before and after function calls but is that the right way to do it?

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

I have MANY php scripts running - some from visitors, others from cron jobs - and I need to find which one(s) of them is leaking memory and fix it ASAP but I don't even know how to determine if a given function is leaking memory or not.

我有很多 php 脚本正在运行——有些来自访问者,有些来自 cron 作业——我需要找出其中哪些正在泄漏内存并尽快修复它,但我什至不知道如何确定给定的函数是否是是否泄漏内存。

I have tried printing memory_get_usage before a function call and after, and it goes up, but then if I call the function more than once, it doesn't go up anymore. Can someone please explain this and tell me how I can simply and easily tell if a PHP function has a memory leak?

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

采纳答案by MatthiasLaug

You could do various things, but first you should try to avoid the creation of memory leaks in the first place.

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

Let me clarify: PHP is a scripting language and it is not designed for long running scripts, so it's memory management is not the best on the market. But why should it be? It's purpose is to be called on a request level so its running scope is quite small (not more than 2 - 3 seconds). Everything else should be put in the background.

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

What can I do against memory leaks?

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

  1. If you are at a version below 5.4 you need to take care of circle references, since those are not garbage collected.

  2. If you need a script to be run continuously, you might think about a different approach. Do try a while(true)implementation, but wrap supervisor(http://supervisord.org) around your script, and let it be called after it ends. That way you make 100% sure you never get memory leaks.

  3. You could use xdebugto profile your scripts one by one and find out, where a lot of memory is consumed.

  4. You could implement a destructor to unset all you references if the class is not of any need anymore.

    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. Read through the PHP documentation regarding garbage collection http://us3.php.net/manual/en/features.gc.php

  6. Avoid global variables, because those are never garbage collected and need to be unsetexplicitly. If you are using a Framework like ZF or Symfony that might not be possible, since you would break functionality if you do.

  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();
                }
            }
        }
    }
    
  5. 通读有关垃圾收集的 PHP 文档http://us3.php.net/manual/en/features.gc.php

  6. 避免使用全局变量,因为它们永远不会被垃圾回收,需要unset显式地进行回收。如果您使用的是像 ZF 或 Symfony 这样的框架,那可能是不可能的,因为如果这样做,您会破坏功能。

Last but not least I want to emphasize once again, PHP is not suited for long running scripts! If you have things to do, that need to run continuously you should not crumble your head with memory leaks in PHP, but take the time to learn a more sophisticated language like JAVA or C#.

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

回答by Slam

Look at this php-extension: https://github.com/arnaud-lb/php-memory-profiler. You will can dump information in different formats and simple analyze it by some tools such as: Google Performance Tools, KCacheGrindor QCacheGrind.

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

回答by red_led

I found method which works pretty well for me:

我找到了对我很有效的方法:

  1. Install "php-memprof" extention. In you can Ubuntu run:

    sudo pecl install memprof

  2. Install "google-perftools". Again for Ubuntu:

    sudo apt-get install google-perftools

  3. Add this code to begining of your script:

    if (function_exists('memprof_enable')) {
        memprof_enable();
    }
    
  4. And this aroud place were you expexct to find memory leak:

    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. ";
    }
    

    In my case it was inside big cycle every 100 runs.

  5. Run google-pprofcomparing 2 memory dumps:

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

    This will open svg image like this in your browser:

    sample from doc

    Description of numbers and names inside you can find in gperftools documentation

  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 图像:

    来自文档的样本

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

P.S.Fixing leaks on php-level will not guarantee you that there are no memory-leaks in interpreter. In my case I end up just with restarting sctipt in longer periods.

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

回答by Hashbrown

I'm not an expert on memory usage, but maybe this method will help you detect the problematic scripts:

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

Get information: 1. Use the apache access log files 2. Create your own memory usage log file (http://www.webhostingtalk.com/showthread.php?t=617742)

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

Check the time when the memory usage goes up and compare to the apache access log.

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

It will at least give you information whether the usage goes up slowly and constant or if it starts at a certain point.

它至少会为您提供信息是使用量是缓慢而稳定地增加还是从某个点开始。

Good luck!

祝你好运!