这不是一个可以通过更改配置选项来解决的问题。
更改配置选项有时会产生积极影响,但也很容易使事情变得更糟,或者根本不起作用。
错误的本质是这样的:
#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 中调试的问题,它们绝对需要内部开发人员的注意。
行动方针应该是:
- Open a bug report on http://bugs.php.net
- 如果您有段错误,请尝试提供回溯
- 包含尽可能多的配置信息,尤其是当您使用 opcache 时,包含优化级别。
- 继续检查错误报告是否有更新,可能需要更多信息。
- If you have opcache loaded, disable optimizations
- 我并不是选择 opcache,它很棒,但已知它的一些优化会导致错误。
- 如果这不起作用,即使您的代码可能较慢,也请先尝试卸载 opcache。
- 如果其中任何一个更改或解决了问题,请更新您所做的错误报告。
- Disable all unnecessary extensions at once.
- 开始单独启用所有扩展,并在每次配置更改后进行彻底测试。
- 如果您发现问题扩展,请使用更多信息更新您的错误报告。
- Profit.
可能没有任何利润...我一开始就说过,你也许可以通过搞乱配置找到一种方法来改变你的症状,但这非常碰运气,下次你再遇到这种情况时就没有帮助了。相同zend_mm_heap corrupted
消息,只有这么多配置选项。
当我们发现错误时,创建错误报告非常重要,我们不能假设下一个遇到错误的人会这样做......更有可能的是,实际的解决方案绝不是神秘的,如果你让正确的人意识到了这个问题。
USE_ZEND_ALLOC
如果你设置USE_ZEND_ALLOC=0
在环境中,这会禁用 Zend 自己的内存管理器; Zend 的内存管理器确保每个请求都有自己的堆,所有内存在请求结束时都被释放,并针对分配适合 PHP 大小的内存块进行了优化。
禁用它将会禁用这些优化,更重要的是,它可能会造成内存泄漏,因为有很多扩展代码依赖于 Zend MM 在请求结束时为它们释放内存(啧啧)。
也可能是hide症状,但系统堆可能会以与 Zend 堆完全相同的方式被损坏。
看似宽容一些,或许宽容一些,但要解决问题的根源,这不可以.
完全禁用它的能力是为了内部开发人员的利益;你应该never在禁用 Zend MM 的情况下部署 PHP。