这不是必须通过更改配置选项来解决的问题。
更改配置选项有时会产生积极影响,但也很容易使事情变得更糟,或者什么都不做。
错误的性质是这样的:
#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)。
如果你玩弄这些糟糕的代码,你会发现并非所有那些明显不正确的语句都会导致分段错误(致命的终止错误)。
我在示例代码中明确犯了这些错误,但在内存管理环境中很容易发生相同类型的错误:如果某些代码没有以正确的方式维护变量(或其他符号)的引用计数,例如,如果它释放得太早,另一段代码可能会从已经释放的内存中读取,如果它以某种方式存储地址错误,另一段代码可能会写入无效内存,它可能会被释放两次.. .
这些不是可以在 PHP 中调试的问题,它们绝对需要内部开发人员的注意。
行动方案应该是:
- 在http://bugs.php.net 上打开错误报告
- 如果您有段错误,请尝试提供backtrace
- 包含尽可能多的配置信息,尤其是在您使用 opcache 包含优化级别的情况下。
- 请继续检查错误报告以获取更新,可能需要更多信息。
- 如果您加载了 opcache,请禁用优化
- 我没有选择 opcache,它很棒,但已知它的一些优化会导致错误。
- 如果这不起作用,即使您的代码可能较慢,请先尝试卸载 opcache。
- 如果其中任何一项更改或修复了问题,请更新您提交的错误报告。
- 一次性禁用所有不必要的扩展。
- 开始单独启用所有扩展,在每次配置更改后进行彻底测试。
- 如果您发现问题扩展,请使用更多信息更新您的错误报告。
- 利润。
可能没有任何利润...我一开始就说过,你也许可以通过乱七八糟的配置找到改变症状的方法,但这非常偶然,对下一个没有帮助当您有相同的zend_mm_heap corrupted 消息时,配置选项就这么多。
当我们发现错误时创建错误报告真的很重要,我们不能假设下一个遇到错误的人会这样做......更有可能的是,实际解决方案绝不是神秘的,如果你让正确的人意识到问题。
USE_ZEND_ALLOC
如果在环境中设置USE_ZEND_ALLOC=0,这会禁用 Zend 自己的内存管理器; Zend 的内存管理器确保每个请求都有自己的堆,所有内存在请求结束时都被释放,并且针对分配的内存块进行了优化,大小正好适合 PHP。
禁用它会禁用这些优化,更重要的是它可能会造成内存泄漏,因为有很多扩展代码依赖于 Zend MM 在请求结束时为它们释放内存(啧啧,啧啧)。
它也可能隐藏症状,但系统堆可能会以与 Zend 的堆完全相同的方式被破坏。
它可能看起来更宽容或更宽容,但解决问题的根本原因,它不能。
完全禁用它的能力是为了内部开发人员的利益;您应该永远在禁用 Zend MM 的情况下部署 PHP。