凌晨两点内存泄漏排查全记录

一、凌晨两点,线上服务突然挂了

那天晚上我正睡得迷迷糊糊,手机连续震了好几下,群里炸了锅:线上接口大面积超时,监控面板上一片飘红。我爬起来一看,PHP-FPM 的进程数飙到了上限,每个 worker 的内存占用都异常偏高,日志里刷满了Allowed memory size of 134217728 bytes exhausted。得,内存泄漏,跑不掉了。

说实话,这种问题最磨人。不是一下子崩,而是慢慢把内存吃光,等到某个时刻集中爆发。今天就把我这次排查的完整过程写下来,希望能帮遇到类似问题的朋友少走点弯路。

二、先复现,再谈定位

排查内存泄漏,第一步永远是复现。我先把出问题的接口单独拉出来压测,用ab连续打了几百个请求,然后盯着 PHP-FPM 的进程内存看。果然,单个 worker 的内存从 30MB 一路涨到 100MB 多,而且请求结束后并没有降回去。这就基本坐实了:某个请求周期里,有对象一直没被释放。

为了更直观地观察,我写了个简单的监控脚本,把每个请求前后的内存差打出来:

<?php // 在入口文件开头记录基线 $baseline = memory_get_usage(); // ... 业务逻辑 ... // 在请求结束时对比 error_log('内存增量: ' . (memory_get_usage() - $baseline) . ' bytes'); ?>

跑了几轮之后,日志里能明显看到某些接口的内存增量异常大,而且每次请求都在涨。这就把排查范围缩小到了那几个接口上。

三、顺着调用链找元凶

锁定接口之后,我开始逐段检查代码。翻到一段处理批量导入的逻辑时,发现了一个很典型的坑:循环里不断往数组里塞数据,但数组本身没有清空,导致内存越积越多。类似这样:

<?php // 危险写法:循环里无限累积 $allRows = []; foreach ($fileData as $row) { $processed = processRow($row); // 处理完的数据 $allRows[] = $processed; // 全部攒在内存里 } // 直到循环结束才一次性处理,内存早就爆了 ?>

这种写法在数据量小的时候看不出问题,一旦文件里有几万行,内存就直线上升。修复思路很简单:边处理边释放,不要把所有中间结果都攒在内存里。改成流式处理,每处理完一行就写出去,然后及时清掉引用:

<?php // 安全写法:边读边处理,及时释放 $handle = fopen($filePath, 'r'); while (($row = fgetcsv($handle)) !== false) { $processed = processRow($row); writeToDb($processed); // 立即落库 unset($processed, $row); // 主动释放引用 } fclose($handle); ?>

改完之后再压测,内存增量明显平稳了,不再一路飙升。

四、还有两个容易忽略的坑

除了循环累积,还有两个地方也容易埋雷。一个是静态变量和单例。如果某个类用静态属性缓存了数据,又没做清理,那这些数据会一直活到进程结束。另一个是外部服务调用没超时,比如某个接口请求第三方 API,对方一直不返回,连接和缓冲区就一直占着内存。

排查的时候可以用memory_get_peak_usage()看峰值,再配合gc_collect_cycles()手动触发一次垃圾回收,对比回收前后的内存差,能帮你判断是不是循环引用导致的泄漏:

<?php $before = memory_get_usage(); gc_collect_cycles(); $after = memory_get_usage(); error_log('GC 回收内存: ' . ($before - $after) . ' bytes'); ?>

如果回收前后差得很多,说明代码里存在循环引用,需要检查对象之间的相互持有关系。

五、总结:内存问题要防在日常

这次故障从发现到解决,前后折腾了大半夜。回头想想,其实很多内存泄漏都是编码习惯问题:循环里不注意释放、静态变量乱缓存、外部调用不设超时。与其等线上报警再熬夜排查,不如在写代码的时候就多留个心眼。建议大家在测试环境就养成看内存曲线的习惯,把memory_get_usage()打点加到关键接口里,早发现早处理。

如果你也遇到过类似的内存泄漏问题,欢迎在评论区聊聊你的排查思路,咱们一起交流交流。

相关新闻

dirsearch绕过WAF的五个实战技巧:从UA伪装到网络出口配置

dirsearch绕过WAF的五个实战技巧:从UA伪装到网络出口配置

1. 先弄明白&#xff1a;dirsearch扫描为什么会被WAF拦1.1 一次目录扫描的典型困境做Web目录扫描的人&#xff0c;几乎都用过dirsearch&#xff0c;也几乎都被WAF教育过。我印象很深的一次授权测试&#xff1a;目标是一个带前置WAF的业务系统&#xff0c;我拿到授权后顺手跑了默…

2026/11/14 1:31:20 阅读更多 →
四旋翼ADRC姿态控制:从核心公式到调参实践

四旋翼ADRC姿态控制:从核心公式到调参实践

四旋翼的ADRC姿态控制&#xff0c;总给人一种“玄学调参”的错觉。我最早接触自抗扰控制时&#xff0c;也被满屏的扩张状态观测器、总扰动、带宽法这些名词劝退过&#xff0c;直到真正从标题里那条滚转通道的角加速度方程往下一层层拆&#xff0c;才觉得这事其实比想象中有意思…

2026/11/12 12:45:24 阅读更多 →
2026年新兴CI/CD测试工具全景:五大阵营与选型落地指南

2026年新兴CI/CD测试工具全景:五大阵营与选型落地指南

我在整理2026年新兴CI/CD测试工具全景报告时&#xff0c;最直观的感受是&#xff1a;工具赛道已经从一个“能跑通流水线”的时代&#xff0c;进入了“能让测试活动在流水线里被精确调配”的时代。这份报告不是为了罗列新名字&#xff0c;而是想回答一个更实际的问题&#xff1a…

2026/11/12 19:40:40 阅读更多 →

最新新闻

Electron桌面应用开发实战:从环境搭建到打包发布全流程

Electron桌面应用开发实战:从环境搭建到打包发布全流程

做前端这些年&#xff0c;我大部分时间都在跟浏览器标签页打交道&#xff0c;直到第一次把桌面应用跑起来&#xff0c;才发现原来自己一直被限制在地址栏和刷新按钮围起来的框框里。Electron这个词&#xff0c;最开始是在某段时间频繁出现在同事的讨论里&#xff0c;一个用HTML…

2026/11/13 5:13:25 阅读更多 →
滑盖智能锁T109深度解析:结构原理、安装调试与故障排查

滑盖智能锁T109深度解析:结构原理、安装调试与故障排查

简介&#xff1a;亚太天能T109滑盖智能锁产品说明书是一份PDF格式的官方技术文档&#xff0c;主要面向智能锁安装人员、终端用户及售后维护者&#xff0c;帮助快速掌握产品安装、配置与常见故障处理。全包仅1个PDF文件&#xff0c;大小4.37MB&#xff0c;内容结构清晰&#xff…

2026/11/13 5:13:25 阅读更多 →
PMSM弱磁控制原理与Simulink建模:高速AGV驱动系统设计指南

PMSM弱磁控制原理与Simulink建模:高速AGV驱动系统设计指南

上个月我在调试一台高速AGV的驱动系统&#xff0c;整车要求空载跑到8m/s&#xff0c;电机转速已经拉到额定点&#xff0c;驱动器的输出电压也顶到了母线电压极限附近。再往上踩速度指令&#xff0c;转速纹丝不动&#xff0c;三相电流反而开始震荡。那会儿我的第一反应就是&…

2026/11/13 5:13:25 阅读更多 →
C++核心知识点:从内存管理到STL的实战指南

C++核心知识点:从内存管理到STL的实战指南

C 这门语言我前前后后折腾了十几年&#xff0c;用它写过嵌入式驱动、做过游戏服务端&#xff0c;也搭过基础组件库。每次看到新人一头扎进"C 核心知识点"这个搜索词&#xff0c;我其实挺感慨的——这门语言的难点从来不是语法多&#xff0c;而是知识点之间的耦合太深…

2026/11/13 22:08:41 阅读更多 →
T109滑盖智能锁怎么装?安装调试与故障排查全攻略

T109滑盖智能锁怎么装?安装调试与故障排查全攻略

简介&#xff1a;亚太天能T109滑盖智能锁产品说明书以PDF格式发布&#xff0c;适合智能锁用户、安装人员与售后维修人员阅读。内容围绕T109系列指纹密码刷卡锁展开&#xff0c;先介绍前面板、后面板、锁体、机械钥匙、电池盒盖等部件名称与功能&#xff0c;再给出完整安装步骤&…

2026/11/12 18:22:25 阅读更多 →
LLM知识降熵:从信息垄断到理解权下放

LLM知识降熵:从信息垄断到理解权下放

你有没有过这种经历&#xff1a;翻开源码注释、论文摘要或者一份专业报告&#xff0c;每个字都认识&#xff0c;连在一起却像天书。想找人聊聊&#xff0c;身边要么没人懂&#xff0c;要么懂的懒得跟你解释。后来我试着把同一段内容丢给一个大语言模型&#xff0c;几秒钟之后&a…

2026/11/13 5:13:25 阅读更多 →

日新闻

周新闻

月新闻