要找出运行中的 PHP 进程到底把内存耗在哪里,用 OpenResty XRay 剖析它即可:它会挂载到未经修改的进程上,按占用内存对存活的 PHP 对象排序,并通过数据引用路径把最大的那个对象追溯到确切的源码行——无需修改代码,也无需重启。

本教程将结合 OpenResty XRay 的引导式分析,以及我们自己发明的 PHP 对象内存分布火焰图,完整演示这一过程——案例中,一个 controller 持有的单个 40MB 的 HTML 字符串就是元凶。

现象: PHP 进程在 top 中占用大量内存

先运行 top 命令查看内存占用。可以看到,这个 php 进程消耗了最多的内存——超过了 37MB 的 RSS。

top 输出显示 php 进程占用最多内存,超过 37MB RSS

让我们运行 ps 命令来查看这个进程的更多细节。可以看到,这个进程使用的 PHP 二进制可执行文件,是 Linux 发行版自带的。

ps 输出显示这个进程是标准的 /usr/bin/php 二进制

我们可以使用 OpenResty XRay 来检查这个未经修改的进程。系统可以对它进行实时分析,并找出内存具体耗费在哪里。

用 OpenResty XRay 引导式分析找出最大的 PHP 对象

在浏览器中打开 OpenResty XRay 的 Web 控制台,确保当前分析的机器是正确的。进入 “Guided Analysis” 页面,这里可以看到 OpenResty XRay 能诊断的各类问题。

OpenResty XRay 引导式分析的问题类型,其中包含 High memory usage

选择 “High memory usage”,选中之前的 PHP 应用以及 top 标记出来的那个进程(占用超过 35MB RSS 的那个),保持 PHP 和 C/C++ 两个语言级别都选中,然后开始分析。经过一到两轮分析后,OpenResty XRay 会自动生成一份报告。在 PHP-Land 下,它会展示占用最多内存的 PHP 内存对象引用路径。

OpenResty XRay 报告中排名第一的 PHP GC 对象内存分布引用路径

可以看到,网页的 HTML 源码字符串占用了约 40MB 的内存资源。

占用近 40MB 内存的网页 HTML 源码字符串

productPagecontroller 的一个属性,用于储存这个字符串。

controller 的 productPage 属性,存储着这个 40MB 的 HTML 字符串

Illuminate\Routing\Route 类的主要功能是将 URL 请求映射到 controller 方法。

Illuminate Routing Route 类将 URL 请求映射到 controller 方法

这条数据引用路径,是从下面这张 PHP 语言级别的 GC 对象内存分布火焰图中自动推导出来的。

PHP-Land PHP GC 对象内存分布火焰图,高亮出直达 40MB 字符串的最热调用栈

把最大的对象追溯到它的源码行

从引用路径中复制 productPage 属性,跳转到 PHP 应用的源代码目录,用 grep 命令搜索包含 productPage 变量标识符的文件。

grep 在源码目录中搜索 productPage 变量标识符

这是赋值语句的源码行号——ProdController.php 第 40 行。

grep 输出显示 productPage 赋值语句位于 ProdController.php 第 40 行

用编辑器打开源文件。这里是定义属性的地方。

ProdController.php 源文件中定义 productPage 属性的位置

跳转到第 40 行。$this->productPage = file_get_contents($file_path) 这条语句一次性把整个页面读入内存,从而创建了这个大内存对象。我们建议改用流式响应对内存使用进行优化。

ProdController.php 第 40 行通过 file_get_contents 把整个文件读入 productPage

用 Insights 报告自动监控 PHP 进程内存

OpenResty XRay 也可以自动监控在线进程并显示分析报告。在 “Insights” 页面中,您可以找到以日和周为周期的报告,这样您就不必手动运行引导式分析了——当然,“Guided Analysis” 对于应用的开发和演示仍然很有用。

OpenResty XRay Insights 中该 PHP 应用的每日内存报告

常见问题

如何找出哪个 PHP 进程占用了最多内存?

运行 top 命令——php 进程会显示为占用内存最多的那个,本例中超过 37MB。运行 ps 查看更多细节;这里它是 Linux 发行版自带的标准 PHP 二进制。随后 OpenResty XRay 会分析这个进程,找出内存具体耗费在哪里。

可以在不修改代码、不重启的情况下剖析 PHP 内存占用吗?

可以。OpenResty XRay 会实时分析未经修改的、运行中的 PHP 进程——本例中就是 Linux 发行版自带的标准 PHP 二进制。因为它直接作用于原样的进程,所以您无需修改或重启应用就能看清内存去向。

什么是 PHP 内存对象引用路径?

它是指向占用最多内存的对象的引用路径——本例中,就是存储在某个 controllerproductPage 属性中、近 40MB 的 HTML 字符串,以及负责将请求映射到该 controller 的 Route 类。OpenResty XRay 会从 PHP 语言级别的 GC 对象内存分布火焰图中自动推导出这条路径。

如何降低某个大 PHP 对象导致的高内存占用?

把最大的对象追溯到创建它的源码行——本例中,就是那条对 productPage 属性赋值、创建了大内存对象的语句。OpenResty XRay 的报告建议改用流式响应来优化内存使用。

关于 OpenResty XRay

OpenResty XRay 是一个动态追踪产品,它可以自动分析运行中的应用,以解决性能问题、行为问题和安全漏洞,并提供可行的建议。在底层实现上,OpenResty XRay 由我们的 Y 语言驱动,可以在不同环境下支持多种不同的运行时,如 Stap+、eBPF+、GDB 和 ODB。

关于作者

章亦春是开源 OpenResty® 项目创始人兼 OpenResty Inc. 公司 CEO 和创始人。

章亦春(Github ID: agentzh),生于中国江苏,现定居美国湾区。他是中国早期开源技术和文化的倡导者和领军人物,曾供职于多家国际知名的高科技企业,如 Cloudflare、雅虎、阿里巴巴, 是 “边缘计算“、”动态追踪 “和 “机器编程 “的先驱,拥有超过 22 年的编程及 16 年的开源经验。作为拥有超过 4000 万全球域名用户的开源项目的领导者。他基于其 OpenResty® 开源项目打造的高科技企业 OpenResty Inc. 位于美国硅谷中心。其主打的两个产品 OpenResty XRay(利用动态追踪技术的非侵入式的故障剖析和排除工具)和 OpenResty Edge(最适合微服务和分布式流量的全能型网关软件),广受全球众多上市及大型企业青睐。在 OpenResty 以外,章亦春为多个开源项目贡献了累计超过百万行代码,其中包括,Linux 内核、Nginx、LuaJITGDBSystemTapLLVM、Perl 等,并编写过 60 多个开源软件库。

关注我们

如果您喜欢本文,欢迎关注我们 OpenResty Inc. 公司的博客网站 。也欢迎扫码关注我们的微信公众号:

我们的微信公众号

翻译

我们提供了英文版原文和中译版(本文)。我们也欢迎读者提供其他语言的翻译版本,只要是全文翻译不带省略,我们都将会考虑采用,非常感谢!