PHP处理视频时内存不释放的主因是ffmpeg子进程残留、文件句柄未关闭、ffprobe冗余解析及分块合并时一次性加载。需显式close()、fclose()、unlink(),复用实例,流式读写,精简JSON解析,并监控RSS与进程状态。

video_convert() 类函数调用后内存不释放
PHP 处理视频文件时,ffmpeg 或 ffprobe 的 PHP 封装(如 PHP-FFMpeg 库)常在后台启动子进程并持有大量资源句柄。即使命令执行完毕,PHP 不会自动清理这些外部进程的内存映射或缓冲区。
常见错误现象:memory_get_usage(true) 看似正常,但 ps aux | grep ffmpeg 显示残留进程,top 中 PHP 进程 RSS 持续上涨。
- 调用
FFMpeg::open()后必须显式调用$video->close()或unset($video),否则底层Process实例和临时文件句柄不会释放 - 避免在循环中反复
new Video:改用单例复用实例,或每次处理完立即gc_collect_cycles() - 不要依赖
__destruct()自动清理——CLI 下 GC 触发时机不可控,需手动干预 - 临时文件务必指定明确路径并主动
unlink(),尤其->save()生成的中间帧或缩略图
fopen('video.mp4', 'rb') 后未 fclose 导致句柄泄漏
直接用 fopen() 读取视频二进制流虽绕过扩展依赖,但极易因忘记 fclose() 导致系统级文件描述符耗尽,继而触发 Too many open files 错误,间接推高内存占用(内核为每个打开文件维护缓冲区)。
- 必须配对使用:
$fp = fopen(...); ...; fclose($fp);,不能只靠脚本结束自动回收 - 推荐用
try...finally(PHP 8.0+)或register_shutdown_function()保底关闭 - 避免在函数内多次
fopen()而无对应fclose(),尤其在分块读取逻辑中 -
stream_get_contents($fp)会把整个流加载进内存,大视频慎用;改用fread($fp, $chunk_size)循环读取
getVideoInfo() 返回数组嵌套过深导致内存翻倍
调用 ffprobe 解析视频元数据时,json_decode($output, true) 生成关联数组,其键名重复存储、结构冗余,比对象模式多占约 15–20% 内存,且对大视频(如含数百个 GOP 或字幕轨道)尤为明显。
立即学习“PHP免费学习笔记(深入)”;
- 改用
json_decode($output, false)返回stdClass对象,减少键名字符串副本 - 只提取真正需要的字段,例如用
-v quiet -show_entries stream=width,height,duration -of json限定输出范围 - 避免将完整
ffprobe输出存入全局变量或静态属性——生命周期越长,越难被 GC 回收 - 处理完立刻
unset($info),特别是批量处理多个视频时
分块上传合并时 file_put_contents() 造成内存暴涨
视频分块上传服务端合并阶段若用 file_put_contents($final, file_get_contents($part), FILE_APPEND),每次调用都会把整个目标文件读入内存再追加,1GB 视频可能触发数 GB 内存峰值。
- 改用流式追加:
$fp = fopen($final, 'ab'); fwrite($fp, file_get_contents($part)); fclose($fp); - 更稳妥做法是
fopen($part, 'rb')+stream_copy_to_stream(),避免一次性加载分块内容 - 合并完成后立即
unlink()所有.part*文件,防止磁盘空间与 inode 占用累积 - 注意
opcache.enable_cli=0—— CLI 模式下 OPcache 默认关闭,但若误启,缓存的临时文件路径可能干扰后续操作
ffmpeg 子进程、未释放的文件句柄、以及 ffprobe 输出的冗余结构。盯住 memory_get_usage(true) 和 ps 输出的 RSS 对比,才能准确定位到底是 PHP 在吃内存,还是外部工具在赖着不走。



















