站长资讯网
最全最丰富的资讯网站

定位分析内存泄漏的原因和后果

定位分析内存泄漏的原因和后果

内部泄漏错误代码:

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

观察php程序内存使用情况

php提提供了两个方法来获取当前程序的内存使用情况。
memorygetusage(),这个函数的作用是获取目前PHP脚本所用的内存大小。

memorygetpeak_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可用内存上限的数据导致内存耗尽

实例:

<?php  ini_set('memory_limit', '128M');   $string = str_pad('1', 128 * 1024 * 1024);     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 查询返回大量数据、一次把大文件读取进程序等。

解决方法:

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

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

<?php   //php7 以下版本通过 composer 引入 paragonie/random_compat ,为了方便来生成一个随机名称的临时文件   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);

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

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

<?php   ini_set("memory_limit",'1M');     $string = str_pad('1', 1* 750 *1024);   $string2 = $string;  $string2 .= '1';     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_heap corrupted

问题现象:局部代码执行过程中占用内存翻倍。

问题分析:
php 是写时复制(Copy On Write),也就是说,当新变量被赋值时内存不发生变化,直到新变量的内容被操作时才会产生复制。

解决方法:

及早释放无用变量,或者以引用的形式操作原始数据。

<?php   ini_set("memory_limit",'1M');     $string = str_pad('1', 1* 750 *1024);   $string2 = $string;  unset($string);   $string2 .= '1';     <?php   ini_set("memory_limit",'1M');     $string = str_pad('1', 1* 750 *1024);   $string2 = &$string;   $string2 .= '1';     unset($string2, $string);

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

情景还原:因配置不合理导致内存不够用,2G 内存机器上设置最大可以启动 100 个 php-fpm 子进程,但实际启动了 50 个 php-fpm 子进程后无法再启动

赞(0)
分享到: 更多 (0)

网站地图   沪ICP备18035694号-2    沪公网安备31011702889846号