PHP-FPM执行大Excel导入后未释放内存的原因及解决方法
问题分析与解决方案
问题原因
- PHP内存管理特性:PHP-FPM进程是常驻内存的,处理大内存任务(如20万条Excel导入)时,进程会申请大量内存。PHP内置的
zend_mm内存管理器在释放内存时,只会将内存归还给进程自身的内存池,不会直接返还给操作系统。因此任务完成后,进程仍会维持高内存占用,操作系统层面无法看到内存释放。 - PHP-FPM配置不合理:
pm.max_requests = 6800设置过高,意味着单个PHP-FPM进程要处理6800次请求才会被回收销毁。执行过大内存任务后的高内存进程会持续存活,直到达到该阈值才会自动重启,这就是你需要手动重启才能释放内存的核心原因。另外pm.max_children = 5900的数值远超常规服务器承载能力,会导致系统资源被过度占用,进一步加剧内存积压问题。
手动修复方法
- 平滑重启PHP-FPM:通过重载操作让高内存进程逐步退出,同时启动新进程,不中断正常服务:
如果重载无效,可执行强制重启:systemctl reload php-fpmsystemctl restart php-fpm - 定向清理高内存进程:找出并终止占用内存过高的PHP-FPM进程(避免影响正常进程):
注:# 筛选出内存占用超过500M的PHP-FPM进程并优雅终止 ps aux | grep php-fpm | awk '$6 > 524288 {print $2}' | xargs kill -TERMkill -TERM会让进程处理完当前请求后再退出,比强制杀死(kill -9)更安全,不会中断正在执行的任务。
长期优化建议
- 调低
pm.max_requests:将该值调整为500-1000,让大内存任务后的进程更快被自动回收,内存会被PHP-FPM重新分配给新进程,间接实现内存释放。 - 优化Excel导入逻辑:不要一次性加载20万条记录到内存,改用分批读取(比如每次读取1000条),处理完一批就释放对应内存,从根源上降低内存占用。可借助PhpSpreadsheet的分批读取功能实现。
- 合理配置进程数:
pm.max_children的合理值计算公式为:(服务器总内存 - 系统预留内存 - 数据库等服务占用内存) / 单个PHP进程平均内存占用。例如服务器总内存16G,预留4G给系统和数据库,单个PHP进程平均占20M,那么pm.max_children应设置在600左右,远低于当前的5900。 - 设置合理的PHP内存限制:在
php.ini中配置memory_limit = 1G(根据实际需求调整),避免单个进程无限制占用内存。
内容的提问来源于stack exchange,提问作者Mohsen Davari
相关产品推荐
相关产品推荐

