FrankenPHP 默认用内存缓存小文件上传,超限才落盘;需显式配置 max_memory_upload 和 upload_tmp_dir,并合理设置 memory_limit 以避免 OOM。

FrankenPHP 默认用内存缓存小文件上传,超限才落盘
FrankenPHP 处理 multipart/form-data 上传时,默认不会直接写磁盘,而是先尝试把文件内容读进内存——这个行为由 max_memory_upload 控制,但该配置**不是 php.ini 标准项**,而是 FrankenPHP 自定义的 SAPI 层参数,且默认值极小(实测约 2MB)。一旦单个文件超过此阈值,它才会 fallback 到临时目录落盘。这意味着:上传一个 5MB 文件,若未调大该值,整个文件体可能全驻留内存,叠加并发请求,RSS 迅速飙升。
常见错误现象:frankenphp 进程 RES 内存随上传请求数线性增长,ps aux 看到单进程 RSS 达 300MB+,但 memory_get_usage() 在脚本里却只报几十 MB——说明内存吃在 SAPI 层解析阶段,PHP 脚本根本没机会介入。
- 必须显式设置
max_memory_upload,方式有两种:
– 启动时加参数:frankenphp run --max-memory-upload=32M
– 或在 Caddyfile 的php_server块中配置:max_memory_upload 32M - 该值建议设为略高于你业务中最大单文件尺寸(如允许传 20MB 视频,则设
32M),留出 multipart 边界和元数据余量 - 设得过大(如
512M)反而危险:多个并发上传会把容器内存打满,触发 OOM Killer 直接杀进程,连日志都来不及写
upload_tmp_dir 权限和路径必须显式指定
当上传文件超出 max_memory_upload,FrankenPHP 会转而写入临时目录,此时依赖 upload_tmp_dir 配置。但它**不继承系统默认 /tmp 的宽松权限**,尤其在容器或 systemd 服务中,运行用户(如 www-data)往往无权写宿主机 /tmp,导致报错 Failed to write to temporary directory 或静默失败($_FILES 为空)。
- 不要依赖默认值,必须在
php.ini中显式声明:upload_tmp_dir = "/var/tmp/frankenphp-upload" - 创建该目录并赋权:
sudo mkdir -p /var/tmp/frankenphp-upload && sudo chown www-data:www-data /var/tmp/frankenphp-upload - 容器部署时,确保该路径被挂载且非只读;Docker Compose 示例:
volumes: - "/host/tmp:/var/tmp/frankenphp-upload:rw" - 验证是否生效:在 PHP 脚本中输出
ini_get('upload_tmp_dir'),并确认返回路径可写(可用is_writable()检查)
PHP 层面 memory_limit 不决定上传内存,但影响后续处理
memory_limit 对文件上传过程本身基本无约束——因为上传解析发生在 PHP 用户态代码执行前,由 SAPI 层完成。但一旦进入脚本,常见操作会立刻触发内存暴涨:
立即学习“PHP免费学习笔记(深入)”;
-
file_get_contents($_FILES['file']['tmp_name']):把整个临时文件读入内存,100MB 文件 → 至少 100MB 内存占用 -
imagecreatefromjpeg()或Imagick::readImage():解码后图像内存是原始文件的 3–10 倍(如 5MB JPG 解码成 RGB 图像可能占 60MB) - 将文件内容
json_encode()或存入$_SESSION:复制、序列化双重开销
所以:memory_limit 应按「处理逻辑」而非「上传大小」设置。例如允许传 100MB 视频,但只做 move_uploaded_file() 存储,则 memory_limit = 128M 足够;若还要 FFmpeg 抽帧或生成缩略图,就得结合 opcache.memory_consumption 和实际峰值调高,并改用流式处理(如 fopen() + fread() 分块读)。
监控上传内存的实际占用,别只看 $_FILES
仅检查 $_FILES['file']['size'] 完全无法反映真实内存压力。你需要分层观测:
- OS 层看进程 RSS:
ps -o pid,comm,rss,vsz -C frankenphp,关注rss 值是否随上传并发数跳升 - PHP 脚本内埋点:
memory_get_usage(true)在move_uploaded_file()前后各取一次,差值即本次上传带来的额外内存分配 - 对比
memory_get_peak_usage(true)和ini_get('memory_limit')字节数,若峰值 > 0.8 × limit,说明处理逻辑有优化空间 - 容器环境务必配
mem_limit(如512m),并确保memory_limit≤ 0.8 × 容器限制,留出 FrankenPHP 自身和 OPcache 的余量
最易被忽略的是:FrankenPHP 的多线程模型下,每个线程共享同一进程内存空间,max_memory_upload 是 per-thread 限制,但 upload_tmp_dir 和 opcache 是全局共用的——上传临时文件堆积、OPcache 缓存污染、未关闭的 GD 资源,都会让内存缓慢泄漏,最终在某个上传请求上集中爆发 OOM。



















