7 php 内存泄漏_PHP内存泄漏分析定位

说明:本文来自作者  邹毅 在 GitChat 上分享「  PHP 内存泄漏分析定位」

目录

场景一 程序操作数据过大

场景二 程序操作大数据时产生拷贝

场景三 配置不合理系统资源耗尽

场景四 无用的数据未及时释放

深入了解

php内存管理

php-fpm内存泄露问题

常驻进程内存泄露问题

前言

本文开始撰写时我负责的项目需要用 php 开发一个通过 Socket 与服务端建立长连接后持续实时上报数据的常驻进程程序,在程序业务功能开发联调完毕后实际运行发送大量数据后发现内存增长非常迅速,在很短的时间内达到了 php 默认可用内存上限 128M ,并报错:

Fatal error: Allowed memory size of X bytes exhausted (tried to allocate Y bytes)

我第一反应是内存泄露了,但是不知道在哪。第二反应是无用的变量应该用完就 unset 掉,修改完毕后问题依旧。经过了几番周折终于解决了问题。就决定好好把类似情况整理一下,遂有此文,与诸君共勉。

观察 PHP 程序内存使用情况

php 提提供了两个方法来获取当前程序的内存使用情况。

memory_get_usage(),这个函数的作用是获取目前PHP脚本所用的内存大小。

memory_get_peak_usage(),这个函数的作用返回当前脚本到目前位置所占用的内存峰值,这样就可能获取到目前的脚本的内存需求情况。

int memory_get_usage ([ bool $real_usage = false ] )

int memory_get_peak_usage ([ bool $real_usage = false ] )

函数默认得到的是调用emalloc()占用的内存,如果设置参数为TRUE,则得到的是实际程序向系统申请的内存。因为 PHP 有自己的内存管理机制,所以有时候尽管内部已经释放了内存但并没有还给系统。

linux 系统文件 /proc/{$pid}/status 会记录某个进程的运行状态,里面的 VmRSS 字段记录了该进程使用的常驻物理内存(Residence),这个就是该进程实际占用的物理内存了,用这个数据比较靠谱,在程序里面提取这个值也很容易

场景一:程序操作数据过大

情景还原:一次性读取超过php可用内存上限的数据导致内存耗尽

$string = str_pad('1', 128 * 1024 * 1024);//str_pad函数的作用把字符串填充为新的长度

Fatal error: Allowed memory size of 134217728 bytes exhausted (tried to allocate 134217729 bytes) in /Users/zouyi/php-oom/bigfile.php on line 3

这是告诉我们程序运行时试图分配新内存时由于达到了PHP允许分配的内存上限而抛出致命错误,无法继续执行了,在 java 开发中一般称之为 OOM ( Out Of Memory ) 。

PHP 配置内存上限是在 php.ini 中设置 memory_limit,PHP 5.2 以前这个默认值是 8M,PHP 5.2 的默认值是16M,在这之后的版本默认值都是128M。

问题现象:特定数据处理时可复现,做任何 IO 操作都有可能遇到此类问题,比如:一次 mysql 查询返回大量数据、一次把大文件读取进程序等。

解决方法:

能用钱解决的问题都不是问题,如果程序要读大文件的机会不是很多,且上限可预期,那么通过 ini_set('memory_limit', '1G'); 来设置一个更大的值或者 memory_limit=-1。内存管够的话让程序一直跑也可以。

如果程序需要考虑在小内存机器上也能正常使用,那就需要优化程序了。如下,代码复杂了很多。

require "vendor/autoload.php";ini_set('memory_limit', '128M');//生成临时文件存放大字符串

$fileName = 'tmp'.bin2hex(random_bytes(5)).'.txt';

touch($fileName);for ( $i = 0; $i < 128; $i++) {$string = str_pad('1', 1 * 1024 * 1024);file_put_contents($fileName, $string,FILE_APPEND);

}$handle = fopen($fileName, "r");for ( $i = 0; $i <= filesize($fileName) / 1 * 1024 * 1024; $i++) {//do something

$string = fread($handle, 1 * 1024 * 1024);

}fclose($handle);unlink($fileName);

场景二:程序操作大数据时产生拷贝

情景还原:执行过程中对大变量进行了复制,导致内存不够用。

Fatal error: Allowed memory size of 1048576 bytes exhausted (tried to allocate 768001 bytes) in /Users/zouyi/php-oom/unset.php on line 8 Call Stack: 0.0004 235440 1. {main}() /Users/zouyi/php-oom/unset.php:0 zend_mm_he

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值