突然之间,我的应用程序出现了问题,这是我以前从未遇到的问题.我决定检查Apache的错误日志,我发现一条错误消息"zend_mm_heap corrupted".这是什么意思.
操作系统:Fedora Core 8 Apache:2.2.9 PHP:5.2.6
经过多次试验和错误,我发现如果我增加output_buffering
php.ini文件中的值,这个错误就会消失
我在PHP 5.5下得到了同样的错误,增加输出缓冲没有帮助.我没有运行APC,所以这不是问题.我终于将其追踪到opcache,我只需要从cli中禁用它.有一个特定的设置:
opcache.enable_cli=0
切换后,zend_mm_heap损坏的错误就消失了.
这不是通过更改配置选项必须解决的问题.
更改配置选项有时会产生积极影响,但它可以轻松地使事情变得更糟,或者根本不做任何事情.
错误的本质是这样的:
#include#include #include int main(void) { void **mem = malloc(sizeof(char)*3); void *ptr; /* read past end */ ptr = (char*) mem[5]; /* write past end */ memcpy(mem[5], "whatever", sizeof("whatever")); /* free invalid pointer */ free((void*) mem[3]); return 0; }
上面的代码可以编译为:
gcc -g -o corrupt corrupt.c
使用valgrind执行代码,您可以看到许多内存错误,最终导致分段错误:
krakjoe@fiji:/usr/src/php-src$ valgrind ./corrupt ==9749== Memcheck, a memory error detector ==9749== Copyright (C) 2002-2013, and GNU GPL'd, by Julian Seward et al. ==9749== Using Valgrind-3.10.1 and LibVEX; rerun with -h for copyright info ==9749== Command: ./corrupt ==9749== ==9749== Invalid read of size 8 ==9749== at 0x4005F7: main (an.c:10) ==9749== Address 0x51fc068 is 24 bytes after a block of size 16 in arena "client" ==9749== ==9749== Invalid read of size 8 ==9749== at 0x400607: main (an.c:13) ==9749== Address 0x51fc068 is 24 bytes after a block of size 16 in arena "client" ==9749== ==9749== Invalid write of size 2 ==9749== at 0x4C2F7E3: memcpy@@GLIBC_2.14 (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so) ==9749== by 0x40061B: main (an.c:13) ==9749== Address 0x50 is not stack'd, malloc'd or (recently) free'd ==9749== ==9749== ==9749== Process terminating with default action of signal 11 (SIGSEGV): dumping core ==9749== Access not within mapped region at address 0x50 ==9749== at 0x4C2F7E3: memcpy@@GLIBC_2.14 (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so) ==9749== by 0x40061B: main (an.c:13) ==9749== If you believe this happened as a result of a stack ==9749== overflow in your program's main thread (unlikely but ==9749== possible), you can try to increase the size of the ==9749== main thread stack using the --main-stacksize= flag. ==9749== The main thread stack size used in this run was 8388608. ==9749== ==9749== HEAP SUMMARY: ==9749== in use at exit: 3 bytes in 1 blocks ==9749== total heap usage: 1 allocs, 0 frees, 3 bytes allocated ==9749== ==9749== LEAK SUMMARY: ==9749== definitely lost: 0 bytes in 0 blocks ==9749== indirectly lost: 0 bytes in 0 blocks ==9749== possibly lost: 0 bytes in 0 blocks ==9749== still reachable: 3 bytes in 1 blocks ==9749== suppressed: 0 bytes in 0 blocks ==9749== Rerun with --leak-check=full to see details of leaked memory ==9749== ==9749== For counts of detected and suppressed errors, rerun with: -v ==9749== ERROR SUMMARY: 4 errors from 3 contexts (suppressed: 0 from 0) Segmentation fault
如果你不知道,你已经发现那mem
是堆分配的内存; 堆指的是程序在运行时可用的内存区域,因为程序显式请求它(在我们的例子中使用malloc).
如果您使用可怕的代码,您会发现并非所有明显不正确的语句都会导致分段错误(致命的终止错误).
我在示例代码中明确地做了这些错误,但是在内存管理环境中很容易发生同样的错误:如果某些代码没有以正确的方式维护变量(或其他符号)的引用计数,例如如果它是免费的太早,另一段代码可能从已经免费的内存读取,如果它以某种方式存储地址错误,另一段代码可能写入无效内存,它可能是免费的两次...
这些不是可以在PHP中调试的问题,它们绝对需要内部开发人员的注意.
行动方针应该是:
在http://bugs.php.net上打开错误报告
如果您有段错误,请尝试提供回溯
包括尽可能多的配置信息,特别是如果您使用opcache包括优化级别.
继续检查错误报告以获取更新,可能会要求提供更多信息.
如果加载了opcache,请禁用优化
我不是在选择opcache,它很棒,但已知其中一些优化会导致错误.
如果这不起作用,即使您的代码可能较慢,请先尝试卸载opcache.
如果其中任何一项更改或修复了问题,请更新您所做的错误报告.
一次禁用所有不必要的扩展.
开始单独启用所有扩展,在每次配置更改后进行彻底测试.
如果您发现问题扩展,请使用更多信息更新您的错误报告.
利润.
可能没有任何利润...我在开始时说过,你可能会找到一种通过搞乱配置来改变症状的方法,但这是非常受欢迎和错过,并且在下次你有时没有帮助相同的zend_mm_heap corrupted
消息,只有这么多的配置选项.
当我们发现错误时,我们创建错误报告非常重要,我们不能假设下一个遇到错误的人会去做...更可能的是,实际的解决方案绝不是神秘的,如果你做的话合适的人知道这个问题.
USE_ZEND_ALLOC如果你USE_ZEND_ALLOC=0
在环境中设置,这将禁用Zend自己的内存管理器; Zend的内存管理器确保每个请求都有自己的堆,所有内存在请求结束时都是空闲的,并且针对PHP的内存大小分配进行了优化.
禁用它将禁用这些优化,更重要的是它可能会造成内存泄漏,因为有很多扩展代码依赖于Zend MM在请求结束时为它们释放内存(tut,tut).
它也可能隐藏症状,但系统堆可能会以与Zend堆完全相同的方式被破坏.
它似乎更宽容或更不宽容,但修复问题的根本原因,它不能.
完全禁用它的能力是为了内部开发人员的利益; 永远不应该在Zend MM禁用的情况下部署PHP.
如果您在Linux机器上,请在命令行上尝试此操作
export USE_ZEND_ALLOC=0
检查unset()
s.确保你没有unset()
引用$this
析构函数中的(或等价物),并且析构函数中的unset()
s不会导致同一对象的引用计数降至0.我做了一些研究,发现通常会导致堆的内容腐败.
有关于zend_mm_heap损坏错误的PHP错误报告.有关[2011-08-31 07:49 UTC] f dot ardelian at gmail dot com
如何重现它的示例,请参阅注释.
我有一种感觉,所有其他"解决方案"(更改php.ini
,从源代码使用较少的模块编译PHP等)只是隐藏问题.
对我来说,以前的答案都没有奏效,直到我尝试:
opcache.fast_shutdown=0
到目前为止似乎有效.
我正在使用PHP 5.6与PHP-FPM和Apache proxy_fcgi,如果这很重要......
在我的情况下,这个错误的原因是其中一个数组变得非常大.我已经设置了我的脚本以在每次迭代时重置数组并对问题进行排序.
根据错误跟踪器,设置opcache.fast_shutdown=0
.快速关闭使用Zend内存管理器来清理它的混乱,这会禁用它.