FrankenPHP内存占用高主因是OS进程级膨胀或PHP线程/Worker级泄漏,需分层定位:用ps和/proc/<pid>/status查VmRSS确认真实物理内存占用;监控frankenphp_queue_depth和threads_busy率判断线程池阻塞;在Worker脚本中用memory_get_peak_usage(true)打点识别PHP层内存暴增;检查OPcache配置避免静默内存黑洞。

FrankenPHP 内存占用高,不是 PHP 脚本写得差,而是常驻进程里资源没释放、复用错位或配置失衡——先定位是 OS 进程级膨胀,还是 PHP 线程/Worker 级泄漏,再往下切。
看 ps 和 /proc/<pid>/status 确认真实 RSS 占用
别只信 top 里的 RES 值,它可能含共享内存;VmRSS 才是实际物理内存占用。FrankenPHP 主进程(非 worker)若 RSS 持续 > 300MB,大概率是 Go 层加载了大资源(如 TLS 证书链、大量路由规则)或存在 goroutine 泄漏。
- 查主进程 PID:
pgrep -f "frankenphp\|caddy" - 读真实内存:
cat /proc/$(pgrep -f "frankenphp")/status | grep -E "VmRSS|VmSize" - 对比
VmRSS(实际使用)和VmSize(虚拟地址空间):若前者稳定但后者持续增长 → 可能是 mmap 映射未释放(如大文件读取后未munmap)
监控 frankenphp_threads_busy 和 frankenphp_queue_depth
内存高 + CPU 低,往往是线程卡在 I/O 或下游依赖上,导致请求堆积、内存缓存越积越多。这些指标暴露的是“活体内存压力”,比静态快照更准。
- 访问
/metrics(需 Caddy 启用prometheus指令),重点盯:-
frankenphp_queue_depth> 0 持续 10s 以上 → 新请求进不来,旧请求占着内存不释放 -
frankenphp_threads_busy / frankenphp_threads_total长期 > 0.8 → 线程池饱和,每个线程都在等 DB/cURL/文件锁,内存对象无法及时 GC
-
- 用
htop -p $(pgrep -f frankenphp)查线程状态:大量线程处于S(sleep)而非R(running)→ 不是 CPU 瓶颈,是阻塞等待
在 Worker 脚本里打点 memory_get_peak_usage(true)
这是唯一能确认“PHP 层是否真在吃内存”的方式。memory_get_usage() 返回当前分配量,但 memory_get_peak_usage(true) 才反映实际堆内存峰值(含未释放块),单位是字节,可直接对比 memory_limit。
立即学习“PHP免费学习笔记(深入)”;
- 在控制器入口加:
error_log('peak: ' . memory_get_peak_usage(true)); - 连续发 50 个相同请求,观察日志:
- 峰值阶梯式上升(如 24MB → 32MB → 41MB)→ 全局变量/静态属性残留或 PDO 连接未 close
- 峰值周期性回落(如每 500 请求归零一次)→
max_requests生效,属预期行为 - 单次峰值就超
memory_limit * 0.9→ 某个脚本存在明显内存暴增(如大数组未 unset、SimpleXML 加载超大 XML)
检查 opcache.memory_consumption 是否挤占过多
OPcache 是共享内存段,不计入单个 PHP 请求的 memory_get_usage(),但会吃掉容器总内存。FrankenPHP 默认启用 OPcache,若设得过大(如 512M),又没被充分复用,就会变成“静默内存黑洞”。
- 访问
/frankenphp/phpinfo,查opcache.memory_usage.used_memory和opcache.hits - 压测中
opcache.hits增速缓慢,或used_memory占比长期 opcache.validate_timestamps=1 导致频繁重编译(尤其 Docker 挂载卷下文件 mtime 不准) - 建议值:
opcache.memory_consumption = 128(MB),搭配opcache.max_accelerated_files = 20000,避免碎片化
真正难排查的不是某次内存飙升,而是那些“看起来正常但每次请求都多留几 KB”的累积效应——比如一个未 unset 的临时数组、一个没 reset 的 Doctrine QueryBuilder 实例、或者 file_get_contents() 后忘了 unset($content)。这些痕迹不会报错,却会让 Worker 进程在第 1000 次请求时比第 1 次多占 80MB 内存。



















