max_requests是frankenphp worker模式下最简单可靠的内存兜底机制,通过设定worker处理约500个请求后自动重启,强制释放所有php变量、资源及opcache条目,使内存增长变为阶梯式而非无限累积。

max_requests 是 FrankenPHP Worker 模式下最简单、最可靠的内存兜底机制。它不能替代代码层清理,但能防止因疏漏导致的内存持续增长最终 OOM。
为什么max_requests能缓解一级缓存和监听器泄漏
Doctrine 的 EntityManager 一级缓存、事件分发器的 $optimized 缓存、闭包监听器捕获的容器引用,这些都不会在单次请求结束时自动清空——Worker 进程不销毁,它们就一直堆在内存里。即使你忘了调用 $em->clear() 或没移除闭包监听器,max_requests 500 会让进程在处理完约 500 个请求后强制退出,所有 PHP 变量、资源、OPcache 条目一并释放。
- 它不修复泄漏,但让泄漏“有上限”:内存增长从无限累积变成阶梯式上升(每 500 请求重置一次)
- 对未显式销毁的 GD 图像资源、Imagick 实例、PDO 连接句柄同样生效
- 比依赖
gc_collect_cycles()更可靠——GC 在大堆上可能根本没触发或效果极弱
max_requests 无法覆盖的内存问题
它只管进程重启,不管进程内是否已发生不可逆污染:
- 静态变量(
static $cache = [];)在进程重启前已污染后续所有请求,max_requests不会提前清理它们 - 文件句柄未
fclose()、数据库连接未close(),可能在重启前就耗尽系统限制(Too many open files) - OPcache 编译缓存虽随进程释放,但若
opcache.enable_cli=1开启,类定义会锁死在共享内存中,重启也无效 - 日志处理器(如
StreamHandler)若缓冲未flush(),重启前可能丢失最后一批日志
设多少才合理:别只看数字,要看实际内存曲线
500 是常见起点,但必须结合压测数据调整:
- 用
memory_get_peak_usage(true)在每次请求末尾打点,观察内存是否线性上涨;若每 100 请求涨 20MB,那max_requests 300比 500 更安全 - 避免设过高(如 2000):PHP 垃圾回收在超大堆(>1GB)上效率断崖下跌,反而更容易触发
Allowed memory size exhausted - 不要设为 0 或 -1:FrankenPHP 不支持无限运行,这等于主动放弃兜底
- 配合
memory_limit使用,确保max_requests × memory_limit (例如 8 核机器设 16 个 worker,每个 <code>memory_limit 512M,总内存占用不能超 8GB)
真正难的不是设 max_requests,而是确认你的代码在它触发前,有没有把 $_SESSION、$_SERVER['REQUEST_URI']、ORM 实体、全局日志实例这些都重置干净——否则每次重启后,第一个请求就可能带着上一轮的脏状态进来。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











