35

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

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

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

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

4

4 回答 4

28

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

让我澄清一下: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 之类的框架,这可能是不可能的,因为这样做会破坏功能。

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

于 2013-04-26T17:26:28.637 回答
6

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

于 2014-07-01T18:52:26.097 回答
6

我发现对我来说效果很好的方法:

  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 文档中找到内部数字和名称的描述

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

于 2016-03-29T15:30:38.257 回答
1

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

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

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

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

祝你好运!

于 2013-05-03T14:33:11.553 回答