【问题标题】:How to find which PHP script is leaking memory?如何找到哪个 PHP 脚本正在泄漏内存?
【发布时间】:2013-04-10 16:52:23
【问题描述】:

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

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

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

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

【问题讨论】:

  • 我正在考虑这样做...将整个内容复制到另一台服务器,一次只运行一个,看看是什么原因造成的。虽然昂贵且耗时...没有办法在 PHP 中对内存进行单元测试?
  • 我不知道。我也很好奇。我投了你一票,也许我们都会发现。
  • 我同意@TomášZato。您可以使用auto_append_file 脚本和memory_get_* 方法来记录您的所有脚本并注意重的脚本。
  • 您是否尝试过使用ps 监控顶级内存使用者?即一个简单的 cron 作业来转储 ps 的输出(带有适当的标志),这样您就可以看到哪些进程每分钟使用的内存最多。检查您对ps 的风格,以按内存使用情况排序并输出完整的进程详细信息(例如进程+完整的命令行和环境。)

标签: php memory-management memory-leaks


【解决方案1】:

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

让我澄清一下: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();
                }
            }
        }
    }
    
  5. 阅读有关垃圾回收的 PHP 文档http://us3.php.net/manual/en/features.gc.php

  6. 避免使用全局变量,因为这些变量永远不会被垃圾回收并且需要明确地unset。如果您使用 ZF 或 Symfony 之类的框架,这可能是不可能的,因为这样做会破坏功能。

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

【讨论】:

  • 引用的变量呢?
  • 您的答案已有 5 年历史,但仍然相关。我已经使用 PHP 10 多年了,当涉及到大型应用程序时,PHP(任何版本)都不会出现在您的列表中。
  • 我不同意 PHP 不是一门伟大的语言。我从 1981 年开始编写代码。我可以用 Java、C# 等编写代码。大多数人对 PHP 有偏见。 Facebook 是 PHP(重新命名?)! Java 在学术界得到推广。甚至 BASIC 也是很棒的语言。每种语言都有其优点和缺点。Java 的开发 ($$$) 开销比 PHP 多 30% 到 60%!尝试在 Z-80 中编码。 80x86、MX6800x 等......这完全取决于你是多么优秀的程序员(工程师)!
【解决方案2】:

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

【讨论】:

    【解决方案3】:

    我找到了非常适合我的方法:

    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 documentation找到

    P.S. 修复 php 级别的泄漏并不能保证解释器中没有内存泄漏。就我而言,我最终只是在更长的时间内重新启动 stipt。

    【讨论】:

      【解决方案4】:

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

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

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

      它至少会为您提供有关使用量是缓慢而持续上升还是从某个点开始的信息。

      祝你好运!

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2023-03-16
        • 2011-02-05
        • 1970-01-01
        • 2011-02-05
        • 1970-01-01
        • 2013-01-27
        • 2011-01-09
        • 2013-04-04
        相关资源
        最近更新 更多