- 本文地址: https://www.laruence.com/2009/11/27/1164.html
- 转载请注明出处
PHP 5.2x中, 由于错误的选用了zend_atoi, 导致memory_limit不能设置为超过4G的值.
今天同事分享给我一个问题(thans to yanmi), 一段代码(PHP 5.2.11 Linux/X86_64),设置memory_limit为4096M会导致内存耗尽, 而设置4095M就不会. 奇怪的问题呵.
那是怎么回事呢?
问题的原因也很简单, 在PHPSRC/main.c中定义的memory_limit设置项的处理器OnChangeMemoryLimit中, 并没有检测当前的机器字长, 而统一使用了zend_atoi来做为字符串数字化, 这个问题在PHP5.3的版本中已经修正(换成了zend_atol):
static PHP_INI_MH(OnChangeMemoryLimit) { if (new_value) { PG(memory_limit) = zend_atoi(new_value, new_value_length); } else { PG(memory_limit) = 1<<30; /* effectively, no limit */ } return zend_set_memory_limit(PG(memory_limit)); }
而, 顾名思义么, atoi是转成整形, 4096M是2的32次方, 发生溢出, 继而环绕成结果为0, zend_atoi代码如下:
ZEND_API int zend_atoi(const char *str, int str_len) { int retval; if (!str_len) { str_len = strlen(str); } retval = strtol(str, NULL, 0); if (str_len>0) { switch (str[str_len-1]) { case 'g': case 'G': retval *= 1024; /* break intentionally missing */ case 'm': case 'M': retval *= 1024; /* break intentionally missing */ case 'k': case 'K': retval *= 1024; break; } } return retval; }
最后在zend_set_memory_limit的时候, 会错误的设置memory_limit为mm_heap的blok_size, 那结果就肯定远远小与你所预期的4096M了
.... AG(mm_heap)->limit = (memory_limit >= AG(mm_heap)->block_size) ? memory_limit : AG(mm_heap)->block_size; ...
最后, 如果是32位的机器, 那确实不算bug, 但现在的机器很多都64了, 最大内存也不再是4GB了, PHP也要与时俱进啊.
PS, 多看别人的代码是有好处的, 今天有学会了intentionally这个单词, ^_^.
@Anonymous 恩, 这个只是就问题而说问题, 再说,设置成4096,不代表就要用4096么, ;_)
脚本语言估计是不会 要 4G 内存的了。
占用 4G 内存的程序大概需要优化了……
另:1<<30是多大?
————————–
应该是pow(2,30)吧,2的30次方。
4个G内存消耗确实不常见,但有时需要遍历一些数据库操作大数据集的时候还是比较容易碰到的。
分析的不错!另:1<<30是多大?
果然, 今天我也学到了 intentionally 这个单词。 🙂
@Anders 呵呵, 现在的服务器都不止4G的内存了…, 不过这个bug确实影响不大..
真的使用了4G内存?
那我们这些开发机只有512M内存的人怎么活啊?