解决PHP内存泄漏问题

4
我有一个PHP脚本,它无限期地运行(无限主事件循环),处理来自Twitter的流入推文并将它们存储到MySQL中。然而,我似乎无法控制它的内存使用情况。我发现了三种测量内存使用情况的方法:
1. `memory_get_usage()` - 报告约为4.0 MB 2. `memory_get_usage(true)` - 报告约为7.5 MB 3. `exec("ps -o rss -p " . getmypid(), $memOutput);` - 报告一个线性增长的数字,在60分钟内迅速增长到数百MB,并继续消耗内存,直到脚本被强制终止。
我的问题是:
1. 这三种测量方法之间的实际区别是什么? 2. 如果前两种方法相对稳定,但第三种方法像这样失控,这意味着什么?
值得一提的是,我正在使用带有Zend Framework 1.x和许多Zend_Db活动的PHP 5.3。脚本在CLI SAPI下运行。没有使用Zend_Db_Profiler。我还有一个完全不使用数据库的第二个无限运行脚本,内存使用保持不变。因此,它似乎与数据库相关,可能是我PHP设置中使用的MySQL扩展,或者可能是Zend_Db。我在自己的代码中非常小心地避免了粗心缓存对象,尽管我没有对Zend的代码本身这样做。
我尝试让我的脚本调用`gc_enable()`,并定期运行`gc_collect_cycles()`,但这没有帮助。
有什么想法吗?
编辑:我打算尽快对这段代码进行分析,但与此同时,我注意到即使是不涉及数据库的脚本也会泄漏内存。但是它们的速度要慢得多,只有在比较几天的内存使用情况时才变得明显。

1
你已经采取了哪些步骤来回收不再需要的对象所占用的内存? - Hanky Panky
1
首先,我尽可能地避免使用对象,而是使用数组来暂存我的数据。但是对于对象(例如Zend_Db返回的表行),我没有采取任何特殊措施来回收它们的内存。我的理解是,当它们超出作用域(即方法结束时),并且不再保留对这些对象的引用时,它们就可以通过PHP的垃圾回收进行回收。我没有保留任何引用。但我意识到Zend_Db可能会这样做。 - curtisdf
1个回答

2
“我不能在这里告诉你确切的答案,因为你需要自己进行分析。从你所说的内容来看,似乎指向Zend的DB层,但是除非你进行分析,否则无法确定。 ;)”
“在UNIX / Linux上(我希望你以正确的方式运行PHP-UNIX / Linux方式:D),有一些非常有用的工具可以在系统级别上对此类应用程序进行分析,并检查PHP应用程序中的实例化和内存消耗。 你可以使用 Valgrind获得一些信息,例如:”
valgrind --tool=callgrind --dump-instr=yes -v --instr-atstart=no /usr/sbin/apache2 -X

请注意,Valgrind是一组工具,这里我们使用的是“callgrind”工具——它提供了Cachegrind所有信息以及有关调用图的额外信息。这将创建一个名为callgrind.out的文件或一组文件,我记不清了。无论如何,您现在可以使用Kcachegrind来可视化收集到的信息:
kcachegrind callgrind.out

您将看到一个呼叫可视化和应用程序某个部分使用的内存百分比。例如: Wordpress memory profile 您还可以尝试 Valgrind 套件中的其他工具,如Memcheck,以查看所有内存读写和 malloc/new/free/delete 的调用。
我第一次了解 Valgrind 是在尝试对我的 Linux 服务器进行配置文件分析时。然后我做了一些研究,发现它是分析 PHP 应用程序的非常好的工具... 这里有一次非常好的讲座。我使用了其中的一些示例。看看吧!
在对应用程序进行配置文件分析后,请回来提供有关结果或观察的信息。我很有兴趣分析这些数据。希望这有所帮助。;)
编辑: 我现在想起来我漏掉了一些东西。 :D 你也可以尝试使用APD,它是一个Zend扩展,也可能提供有用的信息。我个人没有使用过,但在互联网上有一些很好的例子。
另一个选项是Xhprof - 分层分析器。您可以使用它来收集不同的指标。最终应该使用这些工具的组合。如何和为什么取决于您。

1
感谢Borislav提供的指导。我还没有试过,但我会在有机会时尝试一下。同时,我已经重写了我的脚本,使得与数据库相关的部分被作为短暂的单独进程调用。这有效地解决了内存问题。 - curtisdf
对我来说,仍然令人困惑的是,PHP的用户空间内存使用函数显示稳定和可预测的值,而仅外部进程显示的内存使用情况(例如命令行上的ps)才是失控的。这让我认为这可能根本不是用户空间的PHP代码问题,而是我正在使用的PHP或某些扩展的更低级别问题。使用您提到的工具对代码进行分析将非常有趣。 - curtisdf
@curtisdf 是的,确实很有趣。之后你愿意分享数据吗?它不会包含任何代码。而且它可能真的是一些外部实体。 无论如何,PHP是一种脚本语言,由于其设计问题,长时间运行的脚本一直是一个问题。 另一个尝试的方法是"异步PHP" - 以react为例。它可能是基于事件的长时间运行脚本的解决方案。 - Borislav Sabev
1
我很乐意在这里发布我的发现,尽管目前问题已经避免了,而且我还有很多事情要做,所以我不确定什么时候才能处理它。希望在接下来的几天或本周末之内能够解决。 - curtisdf

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接