PHP -fpm 服务器内存占用陡坡式上涨,请问如何彻底解决?

2020-11-19 11:04:56 +08:00
 reyleon
我手上维护着两台 Web 服务器,后端运行的是 PHP 接口服务,服务器配置为 8 核 16G,PHP 版本为 5.6.40 ,运行在 PHP-FPM 模式下,php-fpm 进程的内存使用情况随着请求数的增加而不断上涨,系统可用内存呈陡坡式下降,一不留神就会有内存告警但我们又处理不够及时而导致业务奔溃的可能。

这个问题让我黯然伤神,不知该如何是好?

我查了一下,几乎全网都在告诉你调整 php-fpm 运行模式,如 pm 改为 dynamic 或 static 以及调整 php-fpm 子进程数量,或者就是让你调低 pm.max_requests 的值,让它达到请求数时自动销毁进程以释放内存。

我们就是这么做的,虽然有点用,但这明显不是治本的办法,这只是一种妥协。

让我疑惑的是,对于内存的释放,我也查到不同的说法:

说法一(来自 Swoole 官方微信公众号)

FPM 自带黑魔法,传统的跑在 FPM 下的 PHP 代码是没有“内存泄漏”一说的。因为 PHP 内核有一个关键函数叫做 php_request_shutdown,此函数会在请求结束后,把请求期间申请的所有内存都释放掉,这从根本上杜绝了内存泄漏。


说法二(来自 PHP 开发组核心成员博客)

PHP 之所以会在请求结束后正确的释放掉所有的资源,内存,这是因为当我们在脚本中使用新的内存的时候,PHP 会向 OS 申请一大块内存(ZEND_MM_SEG_SIZE 大小),然后分给你你需要的合适的一块小内存。

当你不使用这块小内存的时候,PHP 也不会返还给 OS,而是保留下来给后续的处理使用。所以,如果你使用完了资源不及时释放,那么后续的逻辑如果请求内存,PHP 发现之前申请的一大块内存已经分光了,它就只好再次向 OS 发起 malloc 调用,得到一块新的大内存。 并且它还需要对这个大内存做一些标记处理。

而如果你使用完资源,及时释放的话,那么下次脚本申请内存的时候,你之前归还的内存块就可以被重复利用,那么也许你的整个脚本只需要和 OS 申请一次内存。

如果你买了一本 PHP 的书,它告诉你: “不用在 PHP 主动释放资源,因为 PHP 会帮你释放”的话,我建议你,烧了它。


说法三(来自 PHP 官方手册)

引用计数系统是 Zend 引擎的一部分,可以自动检测到一个资源不再被引用了(和 Java 一样)。这种情况下此资源使用的所有外部资源都会被垃圾回收系统释放。因此,很少需要手工释放内存。


以上说法,不知道哪个是正确的。但事实是存在的,php-fpm 进程的内存使用就是会上涨。

有没有大神指点一下,如何深入分析内存上涨的原因?假设要深入 PHP 代码,有没有行之有效的分析工具?

(注:我不是 php 程序员)。
5379 次点击
所在节点    程序员
63 条回复
Varobjs
2020-11-20 17:53:47 +08:00
可能 99%是业务代码写的问题

重构吧
liuxu
2021-02-09 01:37:04 +08:00
@liuxu #19 刚刚看了 min_spare_servers 和 max_spare_servers 相关源码,php-fpm 有一个定时器每秒检测一下

如果进程状态为 FPM_REQUEST_ACCEPTING,也就是没有处理请求,处于闲置状态,而且这些闲置进程的数量 idle 超过了 max_spare_servers,就会每秒 kill 掉一个闲置进程

如果闲置进程的数量 idle 小于 min_spare_servers,就会美妙创建一个进程,直到 min_spare_servers 个,但这有个前提是目前正在运行的进程数量小于 pm_max_children,否则不会创建
liuxu
2021-02-09 01:45:14 +08:00
@liuxu #19 s/就会美妙创建一个进程 /就会每秒创建一个进程 /g

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/727028

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX