本书未发布

PHP7 环境下出现 zend_mm_heap corrupted

为了更好的阅读体验,将文档从 Swoole 官方文档 搬运过来。内容比较多,我会慢慢搬运过来,也欢迎大家以提交改进的方式一起帮助我。

PHP7+Swoole开启opcache,运行时出现zend_mm_heap corrupted。这个问题的主要原因是 PHP7 增加了一个优化项,如果 PHP 代码中一个数组只声明一次,并且没有对数据进行修改操作。PHP7 会将此数组转为immutable类型,此数组仅作为只读。

PHP7 的解析器只能识别 PHP 程序中的数组操作行为,但是扩展层对数组的修改无法识别。而SwooleServer->set等方法可能会修改传入的数组,导致出现内存错误。

Immutable数组

$array = array(
    'worker_num' => 1,
    'log_file' => 'swoole.log',
);

Immutable数组

$array = array(
    'worker_num' => 1,
    'log_file' => 'swoole.log',
);

//有修改行为,PHP7 不会优化此数组为只读
$array['daemonize'] = true;

未启用opcache时,Immutable数组即使被修改了,只要PHP代码中没有再操作此数据则不会出现内存错误。一旦开启opcacheImmutable数组会被转存到SharedMemory并进行持久化。这时Swoole修改此数组会使ZendVM发生内存错误,抛出zend_mm_heap corrupted错误。

zval *zsetting = sw_zend_read_property(swoole_server_class_entry_ptr, getThis(), ZEND_STRL("setting"), 1);
if (zsetting == NULL || ZVAL_IS_NULL(zsetting))
{
    SW_MAKE_STD_ZVAL(zsetting);
    array_init(zsetting);
    zend_update_property(swoole_server_class_entry_ptr, getThis(), ZEND_STRL("setting"), zsetting);
}

add_assoc_bool(zsetting, "open_http_protocol", 1);
add_assoc_bool(zsetting, "open_mqtt_protocol", 0);
add_assoc_bool(zsetting, "open_eof_check", 0);
add_assoc_bool(zsetting, "open_length_check", 0);

底层修复
1.9.2版本增加了一个php_swoole_array_separate的宏,用于将Immutable数组分离并重新构建一个非Immutable数组,底层就可以修改这个数组的值了。实现代码:

#define php_swoole_array_separate(arr)       zval *_new_##arr;\
    SW_MAKE_STD_ZVAL(_new_##arr);\
    array_init(_new_##arr);\
    sw_php_array_merge(Z_ARRVAL_P(_new_##arr), Z_ARRVAL_P(arr));\
    arr = _new_##arr;

C扩展中如果需要修改 PHP 代码传入的数组,必须调用php_swoole_array_separate将数组分离。

php_swoole_array_separate(zset);

解决办法
升级到最新版本的swoole,或者关闭opcache扩展,可修改php.ini加入配置项:

opcache.enable_cli = off

本文章首发在 LearnKu.com 网站上。
上一篇 下一篇
讨论数量: 0
发起讨论 只看当前版本


暂无话题~