🔥码云GVP开源项目 12k star Uniapp+ElementUI 功能强大 支持多语言、二开方便! 广告
# PHP7环境下出现zend\_mm\_heap corrupted [TOC] PHP7+Swoole开启`opcache`,运行时出现`zend_mm_heap corrupted`。这个问题的主要原因是PHP7增加了一个优化项,如果PHP代码中一个数组只声明一次,并且没有对数据进行修改操作。PHP7会将此数组转为`immutable`类型,此数组仅作为只读。 PHP7的解析器只能识别PHP程序中的数组操作行为,但是扩展层对数组的修改无法识别。而Swoole的`Server->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代码中没有再操作此数据则不会出现内存错误。一旦开启opcache,Immutable数组会被转存到`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 ~~~