How to find which PHP script is leaking memory?

2019-01-21 11:20发布

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.

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?

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.

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?

4条回答
手持菜刀,她持情操
2楼-- · 2019-01-21 11:43

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, KCacheGrind or QCacheGrind.

查看更多
在下西门庆
3楼-- · 2019-01-21 11:57

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.

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 xdebug to 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 unset explicitly. If you are using a Framework like ZF or Symfony that might not be possible, since you would break functionality if you do.

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#.

查看更多
狗以群分
4楼-- · 2019-01-21 11:59

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-pprof comparing 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

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.

查看更多
【Aperson】
5楼-- · 2019-01-21 11:59

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)

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

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!

查看更多
登录 后发表回答