减少 workers.count 能直接节省约 30–50MB/worker 的常驻内存,1核2G 服务器从默认 4 个 worker 改为 1 个可释放 90–150MB 内存,但实际收益取决于 OPcache 复用率、缓存对齐及 hot_reload 是否禁用;每个 worker 占用源于 PHP 运行时、OPcache 段、框架类加载和扩展初始化,设 opcache.memory_consumption=64 可压至约 30MB,workers.count=1 是 1 核安全底线。

减少 workers.count 能直接节省约 30–50MB/worker 的常驻内存,1核2G 服务器从默认 4 个 worker 改为 1 个,通常可释放 90–150MB 可用内存——但这不是线性缩放,实际收益取决于 OPcache 复用率、框架缓存是否对齐、以及是否禁用 hot_reload 等配套动作。
为什么每个 worker 吃掉 30–50MB?
这不是固定值,而是典型负载下的实测区间。一个 worker 进程启动后会加载:PHP 核心运行时(约 8–12MB)、OPcache 共享内存段(opcache.memory_consumption 配置值的 60–80% 实际占用)、框架类文件(Symfony/Laravel 的 var/cache/prod 编译结果)、以及扩展初始化开销(如 pdo_mysql、mbstring)。若未预热缓存或 OPcache 路径错配,这个数字可能翻倍。
常见错误现象:top 显示多个 frankenphp-worker 进程 RSS 持续在 45MB 左右波动;ps aux --sort=-%mem | head -5 里 FrankenPHP 相关进程长期占满内存前三位。
- 必须确认你看到的是 worker 进程,而不是 Caddy 主进程或日志进程
- 用
frankenphp info查看实际生效的php.ini路径,避免改了系统 php.ini 却没生效 -
opcache.memory_consumption=128时,单个 worker 实际 RSS 很难低于 40MB;设为 64 后可压到 30MB 左右
worker 数量和 memory_limit 不是简单乘法关系
很多人误以为 “workers.count=2 × memory_limit=256M = 512MB”,其实不对。PHP 的 memory_limit 是 per-request 上限,而 worker 进程的常驻内存远小于此——它只包含已加载的代码、OPcache 共享段、以及当前请求未释放的对象。真正决定内存总量的是:workers.count × (基础常驻内存 + OPcache 实际占用)。
立即学习“PHP免费学习笔记(深入)”;
性能 / 兼容性影响:worker 设太少(如 1 个)会导致并发请求排队,frankenphp_queue_depth 上升;设太多(如 4 个跑在 1核2G 上)则 CPU 上下文切换开销剧增,%CPU 长期 95%+,反而降低吞吐。
- 1核机器上,
workers.count=1是安全底线;有 SSD 和低 I/O 延迟时可试=2 - 不要把
memory_limit设得比单个 worker 常驻内存高太多,比如常驻 35MB 却设memory_limit=512M,等于给 OOM 留后门 - 搭配
max_requests 500使用,让 worker 在内存缓慢增长到临界点前主动退出,比硬扛更稳
怎么验证减少 worker 确实省了内存?
不能只看启动瞬间的 free -h,要观察稳定运行后的内存水位。关键指标是 frankenphp_busy_threads 和 frankenphp_total_threads 的比值是否合理,以及 worker 进程 RSS 是否不再阶梯式上涨。
实操建议:
- 启动后等 2 分钟,再执行
ps aux | grep frankenphp-worker | awk '{sum += $6} END {print sum/1024 " MB"}',对比不同workers.count下的结果 - 压测中持续运行
watch -n 1 'curl -s http://localhost/frankenphp/metrics | grep frankenphp_busy_threads',确认 busy 值不长期卡在total-1 - 如果减少 worker 后 QPS 下降超 15%,且
frankenphp_queue_depth持续 > 10,说明瓶颈已从内存转为 CPU 或 I/O,该调的是代码或数据库,不是 worker 数
最易被忽略的一点:worker 数量调低后,opcache.validate_timestamps 和 opcache.revalidate_freq 必须同步检查——否则 Symfony 容器反复重建,单个 worker 内存照样涨到 100MB+,省下的那点内存全被吃回去了。



















