最近我的应用程序出现了之前从未遇到过的问题。我决定查看Apache的错误日志,结果发现一个错误消息,显示"zend_mm_heap corrupted"。这是什么意思。
操作系统:Fedora Core 8 Apache版本:2.2.9 PHP版本:5.2.6
最近我的应用程序出现了之前从未遇到过的问题。我决定查看Apache的错误日志,结果发现一个错误消息,显示"zend_mm_heap corrupted"。这是什么意思。
操作系统:Fedora Core 8 Apache版本:2.2.9 PHP版本:5.2.6
这不是必然可以通过更改配置选项来解决的问题。
更改配置选项有时会产生积极影响,但同样容易使事情变得更糟,或者根本没有任何作用。
错误的性质如下:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
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 函数)。zend_mm_heap corrupted
错误,因为只有那么多的配置选项。USE_ZEND_ALLOC=0
,则会禁用 Zend 自己的内存管理器;Zend 的内存管理器确保每个请求都有自己的堆,所有内存都在请求结束时被释放,并且针对 PHP 正好大小的内存块进行了优化的分配。经过多次尝试,我发现如果在php.ini文件中增加output_buffering
的值,这个错误就会消失。
opcache.enable_cli=0
一旦切换了zend_mm_heap,"zend_mm_heap corrupted" 错误就消失了。
如果你在Linux系统上,尝试在命令行中运行以下命令
export USE_ZEND_ALLOC=0
set USE_ZEND_ALLOC=0
- Nabi K.A.Z.检查 unset()
。确保在析构函数中不要 unset()
对 $this
(或等价物)的引用并且在析构函数中,unset()
不会导致同一对象的引用计数降至 0。我做了一些研究,发现这通常会导致堆损坏。
有一个关于 zend_mm_heap 损坏错误的PHP bug 报告。请参见评论 [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内存管理器来清理其混乱,这会禁用它。我为这个问题苦苦挣扎了一个星期,这个解决方案对我有效,或者至少看起来是这样。
在php.ini
文件中进行以下更改。
report_memleaks = Off
report_zend_debug = 0
Linux ubuntu 2.6.32-30-generic-pae #59-Ubuntu SMP
with PHP Version 5.3.2-1ubuntu4.7
这个方法没有起作用。
于是我尝试使用基准测试脚本,并尝试记录脚本停顿的位置。 我发现在错误之前,一个php对象被实例化,并且完成该对象应执行的操作需要超过3秒的时间,而在之前的循环中最多只需要0.4秒。我已经运行了这个测试很多次,每次都一样。我想,不妨尝试每次重复使用对象,而不是每次都创建新对象(这里有一个长循环)。到目前为止,我已经测试了这个脚本十几次,内存错误已经消失了!
USE_ZEND_ALLOC=0
来获取错误日志中的堆栈跟踪信息,发现了一个错误/usr/sbin/httpd: corrupted double-linked list
,我发现注释掉opcache.fast_shutdown=1
对我有用。 - Spidfire