什么是“zend_mm_heap损坏”的意思

突然之间,我一直有我的应用程序,我从来没有过的问题。 我决定检查Apache的错误日志,并且发现错误消息“zend_mm_heap已损坏”。 这是什么意思。

OS:Fedora Core 8 Apache:2.2.9 PHP:5.2.6

经过大量的试验和错误,我发现如果我增加了php.ini文件中的output_buffering值,这个错误消失了

我在PHP 5.5下得到这个相同的错误,增加输出缓冲没有帮助。 我没有运行APC,所以这不是问题。 我终于把它追踪到opcache ,我只是不得不从cli禁用它。 有一个具体的设置:

 opcache.enable_cli=0 

一旦切换zend_mm_heap损坏的错误消失了。

如果你在Linux上,请在命令行上试试

 export USE_ZEND_ALLOC=0 

检查未unset() 。 请确保不要在析构函数中unset()$this (或等价物unset()引用,并且在析构函数中的unset()不会导致同一个对象的引用计数降到0.我已经做了一些研究并发现这通常会导致堆损坏。

有关于zend_mm_heap损坏错误的PHP错误报告 。 请参阅[2011-08-31 07:49 UTC] f dot ardelian at gmail dot com上的[2011-08-31 07:49 UTC] f dot ardelian at gmail dot com的评论[2011-08-31 07:49 UTC] f dot ardelian at gmail dot com ,了解如何重现它的示例。

我有一种感觉,所有其他的“解决scheme”(更改php.ini ,从less量模块源码编译PHP等)只是隐藏了问题。

这不是必须通过更改configuration选项才能解决的问题。

改变configuration选项有时会产生积极的影响,但它可能会使事情变得更糟,或者什么也不做。

错误的性质是这样的:

 #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)。

如果你使用可怕的代码,你会发现并不是所有那些明显不正确的语句都会导致分段错误(致命的终止错误)。

我在示例代码中明确地提出了这些错误,但是在一个内存pipe理环境中,相同types的错误很容易发生:如果某些代码没有以正确的方式维护variables(或其他符号)的refcount,如果释放它太早,另一段代码可能从已经释放的内存中读取,如果它以某种方式存储地址错误,则另一段代码可能写入无效内存,它可能被释放两次。

这些不是可以在PHP中debugging的问题,它们绝对需要内部开发人员的关注。

行动的过程应该是:

  1. http://bugs.php.net上打开一个错误报告;
    • 如果您有段错误,请尝试提供回溯
    • 包含尽可能多的configuration信息,特别是如果使用opcache包含优化级别。
    • 继续检查错误报告的更新,可能会要求更多的信息。
  2. 如果您已加载opcache,请禁用优化
    • 我没有selectopcache,这很好,但是它的一些优化被认为会导致错误。
    • 如果这不起作用,即使您的代码可能会更慢,请先尝试卸载opcache。
    • 如果其中任何一项更改或修复了问题,请更新您所做的错误报告。
  3. 一次禁用所有不必要的扩展。
    • 开始单独启用所有扩展,在每次configuration更改后进行彻底testing。
    • 如果您发现问题扩展,更新您的错误报告与更多信息。
  4. 利润。

有可能没有任何利润…我刚开始说,你可能会find一种方法来改变你的症状,通过搞乱configuration,但这是非常受打击和错过,并没有帮助下一次你有相同的zend_mm_heap corrupted消息,只有这么多的configuration选项。

当我们发现错误时,创build错误报告是非常重要的,我们不能假定下一个错误发生的人会这样做……更可能的是,实际的解决scheme绝不是神秘的,正确的人们意识到这个问题。

USE_ZEND_ALLOC

如果在环境中设置USE_ZEND_ALLOC=0USE_ZEND_ALLOC=0禁用Zend自己的内存pipe理器; Zend的内存pipe理器确保每个请求都有自己的堆,所有的内存在请求结束时都是释放的,并且为PHP分配大小适当的内存而进行了优化。

禁用它将禁用这些优化,更重要的是它可能会产生内存泄漏,因为有很多扩展代码依赖于Zend MM在请求(tut,tut)结束时为它们释放内存。

它也可能隐藏症状,但是系统堆可能会像Zend的堆一样被损坏。

它似乎更宽容或宽容,但解决问题的根本原因, 它不能

完全禁用它的能力是为了内部开发人员的利益; 您应该永远不要在禁用Zend MM的情况下部署PHP。

在我的情况下,这个错误的原因是其中一个arrays变得非常大。 我已经设置我的脚本来重置每个迭代的数组,并sorting的问题。

我在这个问题上搏斗了一个星期,这对我或者至less是这样

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秒。 我跑了这个testing好几次了,每次都一样。 我以为不是每次都做一个新的对象(这里有一个长的循环),我应该重用这个对象。 到目前为止我已经testing了十多次,内存错误已经消失了!

对于我以前的答案没有任何工作,直到我试过:

 opcache.fast_shutdown=0 

这似乎工作到目前为止。

我正在使用PHP 5.6与PHP-FPM和Apache proxy_fcgi,如果这很重要…

根据bug跟踪器,设置opcache.fast_shutdown=0 。 快速关机使用Zend内存pipe理器来清理它的混乱,这将禁用。

查找任何使用缓冲的模块,并select性地禁用它。

我在CentOS 4.8上运行PHP 5.3.5,完成后我发现eaccelerator需要升级。

我刚刚在我自己的服务器上也遇到了这个问题,根本原因是APC。 我在php.ini文件中注释了“apc.so”扩展名,重新加载了Apache,并且这些站点正好回来了。

我已经尝试了上面的一切, zend.enable_gc = 0 – 唯一的configuration设置,帮助我。

带Suhosin-Patch(cli)的PHP 5.3.10-1ubuntu3.2(built:Jun 13 2012 17:19:58)

我有这个错误使用PHP的Mongo 2.2驱动程序:

 $collection = $db->selectCollection('post'); $collection->ensureIndex(array('someField', 'someOtherField', 'yetAnotherField')); 

^^不起作用

 $collection = $db->selectCollection('post'); $collection->ensureIndex(array('someField', 'someOtherField')); $collection->ensureIndex(array('yetAnotherField')); 

^^工作! (?!)

在PHP 5.3上,经过很多search之后,这个解决scheme对我来说很有效:

我已经禁用了这个页面的PHP垃圾回收 :

 <? gc_disable(); ?> 

到有问题的页面结束,这使得所有的错误消失。

来源 。

我不认为这里有一个答案,所以我会增加我的经验。 我看到了随机的httpd segfaults一样的错误。 这是一个cPanel服务器。 有问题的症状是Apache会随机重置连接(没有收到数据的铬,或连接重置在Firefox)。 这些似乎是随机的 – 大部分时间是有效的,有时却不是。

当我到达现场输出缓冲closures。 通过阅读这个线程,暗示了输出缓冲,我把它打开(= 4096)看看会发生什么。 在这一点上,他们开始显示错误。 这很好,因为现在的错误是可以重复的。

我经历了并开始禁用扩展。 其中,eaccellerator,pdo,ioncube装载机,和大量的看起来怀疑,但没有帮助。

我终于find淘气的PHP扩展名为“homeloader.so”,这似乎是某种cPanel-easy-installer模块。 拆除后,我没有遇到任何其他问题。

在这一点上,这似乎是一个通用的错误信息,所以你的这个答案会随着所有这些答案而变化,你可以采取的最好的行动scheme:

  • 使错误每次都可重复(什么条件?)
  • find共同的因素
  • select性地禁用任何PHP模块,选项等(或者,如果您急于使用,请全部禁用它们以查看是否有帮助,然后有select地重新启用它们,直到它再次中断为止)
  • 如果这不起作用,许多这些答案暗示它可能是代码相关的。 再次,关键是使错误可重复的每个请求,所以你可以缩小它。 如果您怀疑有一段代码正在执行此操作,则错误可重复后,再次删除代码,直到错误停止。 一旦停止,你知道你删除的最后一块代码是罪魁祸首。

如果没有以上所有,你也可以尝试如下的东西:

  • 升级或重新编译PHP。 希望任何错误导致你的问题是固定的。
  • 将您的代码移到不同的(testing)环境。 如果这解决了这个问题,什么改变了? php.ini选项? PHP版本? 等等…

祝你好运。

我认为很多原因会导致这个问题。 在我的情况下,我命名2个同名的类,一个将尝试加载另一个。

 class A {} // in file a.php class A // in file b.php { public function foo() { // load a.php } } 

这在我的情况下会导致这个问题。

(使用laravel框架,运行php artisan db:真实的种子)

我有这个相同的问题,当我有一个memcached会话session.save_path不正确的IP。 将其更改为正确的IP可解决问题。

如果你正在使用特质,并且在课后加载特征(即自动加载的情况),则需要事先加载特征。

https://bugs.php.net/bug.php?id=62339

注意:这个bug是非常非常随机的; 由于它的本质。

对我来说,问题是使用pdo_mysql。 查询返回1960年的结果。 我试图返回1900个logging,它的工作原理。 所以问题是pdo_mysql和太大的数组。 我重写了与原始的MySQL扩展查询,它的工作。

 $link = mysql_connect('localhost', 'user', 'xxxx') or die(mysql_error()); mysql_select_db("db", $link); 

Apache没有报告任何以前的错误。

 zend_mm_heap corrupted zend_mm_heap corrupted zend_mm_heap corrupted [Mon Jul 30 09:23:49 2012] [notice] child pid 8662 exit signal Segmentation fault (11) [Mon Jul 30 09:23:50 2012] [notice] child pid 8663 exit signal Segmentation fault (11) [Mon Jul 30 09:23:54 2012] [notice] child pid 8666 exit signal Segmentation fault (11) [Mon Jul 30 09:23:55 2012] [notice] child pid 8670 exit signal Segmentation fault (11) 

“zend_mm_heap损坏”意味着内存pipe理问题。 可能是由任何PHP模块造成的。 在我的情况下安装APC的工作。 理论上,其他软件包如eAccelerator,XDebug等也可能有所帮助。 或者,如果您安装了这种模块,请尝试closures它们。

我正在写一个PHP扩展,也遇到这个问题。 当我从我的扩展名调用具有复杂参数的外部函数时,会popup此错误。

原因是我没有为extern函数中的参数(char *)分配内存。 如果你正在写同样的扩展名,请注意这一点。

对我来说,是ZendDebugger导致了内存泄露,导致了MemoryManager崩溃。

我禁用了它,我正在寻找一个更新的版本。 如果我找不到一个,我要切换到xdebug …

因为我从来没有find解决scheme,所以我决定升级我的LAMP环境。 我用PHP 5.3.x去Ubuntu 10.4 LTS。 这似乎已经阻止了我的问题。

在我的情况下,我忘了在代码中的以下内容:

  ); 

我玩了一遍又一遍,忘记了这里和那里的代码 – 在一些地方,我得到了堆腐败,有些情况下,只是普通的“seg故障:

 [Wed Jun 08 17:23:21 2011] [注意]儿童PID 5720退出信号分割故障(11)

我在Mac 10.6.7和XAMPP。

我也注意到这个错误和SIGSEGV的运行旧代码,它使用“&”来显式强制引用,而在PHP 5.2 +中运行它。

设置

 assert.active = 0 

在php.ini帮助我(它closures在php5UTF8库types断言和zend_mm_heap corrupted走开)

对我来说,问题是memcached守护进程崩溃,因为PHP被configuration为将会话信息存储在memcached中。 这是吃了100%的CPU和怪异的。 memcached重启后问题消失了。

由于没有其他答案解决它,我有这个问题在PHP 5.4时,我不小心跑了一个无限循环。

一些提示可能有助于某些人

fedora 20,php 5.5.18

 public function testRead() { $ri = new MediaItemReader(self::getMongoColl('Media')); foreach ($ri->dataReader(10) as $data) { // ... } } public function dataReader($numOfItems) { $cursor = $this->getStorage()->find()->limit($numOfItems); // here is the first place where "zend_mm_heap corrupted" error occurred // var_dump() inside foreach-loop and generator var_dump($cursor); foreach ($cursor as $data) { // ... // and this is the second place where "zend_mm_heap corrupted" error occurred $data['Geo'] = [ // try to access [0] index that is absent in ['Geo'] 'lon' => $data['Geo'][0], 'lat' => $data['Geo'][1] ]; // ... // Generator is used !!! yield $data; } } 

使用var_dummp()实际上不是一个错误,它被放置只是为了debugging,将被删除生产代码。 但是zend_mm_heap发生的地方是第二位的。

我在这里的情况是一样的,上面没有什么帮助,而且更严重的检查我发现我的问题,它包括在try(die(header()))之后发送一些输出到缓冲区,在Code中做了这个的人忘了CakePHP的资源并没有做出简单的“return $ this-> redirect($ url)”。

试图重新发明井,这是问题所在。

我希望这有助于某人!