正文
这是告诉我们程序运行时试图分配新内存时由于达到了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。内存管够的话让程序一直跑也可以。
-
如果程序需要考虑在小内存机器上也能正常使用,那就需要优化程序了。如下,代码复杂了很多。
场景二:程序操作大数据时产生拷贝
情景还原:
执行过程中对大变量进行了复制,导致内存不够用。
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),也就是说,当新变量被赋值时内存不发生变化,直到新变量的内容被操作时才会产生复制。
解决方法:
及早释放无用变量,或者以引用的形式操作原始数据。
场景三:配置不合理系统资源耗尽
情景还原:
因配置不合理导致内存不够用,2G 内存机器上设置最大可以启动 100 个 php-fpm 子进程,但实际启动了 50 个 php-fpm 子进程后无法再启动更多进程
问题现象:
线上业务请求量小的时候不出现问题,请求量一旦很大后部分请求就会执行失败
问题分析:
一般为了安全方面考虑, php 限制表单请求的最大可提交的数量及大小等参数,post_max_size、max_file_uploads、upload_max_filesize、max_input_vars、max_input_nesting_level。
假设带宽足够,用户频繁的提交post_max_size = 8M数据到服务端,nginx 转发给 php-fpm 处理,那么每个 php-fpm 子进程除了自身占用的内存外,即使什么都不做也有可能多占用 8M 内存。
解决方法:
合理设置 post_max_size、max_file_uploads、upload_max_filesize、max_input_vars、max_input_nesting_level 等参数并调优 php-fpm 相关参数。
php.ini