[ 生活需要仪式感 ]

0%

记一次php内存泄漏的排查经过

实际问题

在一个项目里,需要后台运行一个php文件,这个php主要功能就是在一个永远为真的循环里,分别从Redis以及Memcache中拉取数据,然后在php中正则匹配后,把数据推回Redis。
在开发环境是没有问题的,但是上线后却发现运行了几分钟后,进程就没有异常报错的退出了,没有触发任何错误日志,让我一脸懵逼(黑人问号脸)


问题分析

确定问题

  • 首先在每次循环里加入计数器,并且使用memory_get_usage()对每次循环的内存量进行记录。
  • 发现程序每次循环次数到24500+就自动退出了,同时内存量也是一路高歌最后涨到127.3M左右就停了。
  • 去到php.ini查找max_memory_limit发现正好是128M。

由此确认,这个文件的异常退出就是由于运行时内存泄漏导致内存消耗量超过php配置文件上限被退出了。

定位问题

通过memory_get_usage()以及用continue;截断代码来不断缩减范围,配合使用来定位内存泄漏具体代码。

1
2
3
4
5
6
7
8
function print_max_memory(){
static $max_memory = 0;
$memory = memory_get_usage();
if($max_memory < $memory){
$max_memory = $memory;
cli_log(sprintf("max_memory : %s", $max_memory));
}
}

问题总结

AnalyzeProcess.php在进程运行时,每一次while循环都发生了内存泄漏,泄漏量在7~8Kb,最后循环次数到达24500+时,达到php配置文件的内存上限128m,被强制退出。


解决问题

- 问题1

起因

使用list函数时,多定义了一个NULL的下标。

list($game, $server, $username, $role, $ip, $content,$tid) = $data;

实际上$data只有[0]-[5]一共6个下标,$tid为第7个NULL的下标,循环运行时出现内存泄漏。

解决方案

去掉这个多余的下标。

- 问题2

起因

foreach时调用了一个空的数组去做preg_match

解决方案

在preg_match匹配前做一个if判断,若为空则跳过该次循环

- 问题3

起因

用if判断了一个数组不存在的下标

解决方案

把该判断放回到正确的循环语句中


问题总结

解决这个问题后,回望代码,其实出现的问题都是因为代码不够健壮,run一次可能问题不大,但是一直while的run就发生内存泄漏问题了。故需要在码代码的时候注意一些细节的位置,让程序能够平稳运行。