PHP扩展开发中的内存管理是核心基础内容,直接关系到扩展的稳定性和性能,Zend引擎为扩展提供了专门的内存管理接口,开发者需要遵循对应的规则来操作内存,避免直接使用标准C库的内存函数引发问题。

PHP扩展内存管理的基础规则
PHP扩展不能直接使用malloc、free等标准C库内存函数,而是要使用Zend引擎提供的内存管理接口,这些接口会和PHP整体的内存管理生命周期绑定,在请求结束后自动回收未释放的内存,减少内存泄漏的风险。
Zend引擎的内存管理接口分为两类,一类是持久化内存接口,一类是非持久化内存接口:
- 非持久化内存:在单个PHP请求处理期间分配,请求结束后会自动释放,适合存放请求级别的数据
- 持久化内存:跨请求存在,不会因为单个请求结束而释放,适合存放全局共享的配置类数据,需要手动释放
常用的内存分配与释放接口
非持久化内存操作
分配非持久化内存可以使用emalloc系列函数,释放对应使用efree,示例如下:
// 分配10个整型大小的非持久化内存
int *num_arr = (int *)emalloc(sizeof(int) * 10);
// 给数组元素赋值
for (int i = 0; i < 10; i++) {
num_arr[i] = i;
}
// 释放内存
efree(num_arr);
持久化内存操作
分配持久化内存使用pemalloc系列函数,释放使用pefree,需要注意pefree的第二个参数要和分配时的一致,标记是否为持久化内存:
// 分配持久化内存,最后一个参数1表示持久化 char *persist_str = (char *)pemalloc(128, 1); // 赋值 memcpy(persist_str, "test_persist", 13); // 释放持久化内存,第二个参数1对应分配时的持久化标记 pefree(persist_str, 1);
引用计数与变量内存管理
PHP的变量本身采用引用计数的方式管理内存,扩展中操作PHP变量时需要遵循引用计数的规则,避免错误修改引用计数导致内存问题。
常用的引用计数操作宏如下:
| 宏名称 | 作用 |
|---|---|
| Z_TRY_ADDREF(zval) | 尝试增加zval的引用计数,避免重复增加 |
| Z_TRY_DELREF(zval) | 尝试减少zval的引用计数,计数到0时释放内存 |
| ZVAL_COPY(zval_dst, zval_src) | 复制zval并设置引用计数 |
示例如下,扩展中复制一个PHP字符串变量的正确方式:
// 假设src_zval是一个已有的字符串zval zval dst_zval; // 复制zval,自动处理引用计数 ZVAL_COPY(&dst_zval, src_zval); // 后续如果不再使用dst_zval,需要减少引用计数 Z_TRY_DELREF(dst_zval);
常见内存泄漏场景与规避方法
场景1:混用标准C库和Zend内存接口
错误示例:使用malloc分配内存后使用efree释放,或者反过来,都会导致内存管理混乱,引发崩溃或泄漏。
规避方法:统一使用Zend引擎提供的对应内存接口,非持久化用e系列,持久化用pe系列。
场景2:引用计数操作错误
比如手动修改引用计数而没有使用对应的宏,或者在不需要增加引用计数的场景下错误增加,导致变量内存无法被正确释放。
规避方法:操作zval的引用计数时,优先使用Zend提供的封装宏,不要直接修改refcount__gc字段。
场景3:持久化内存未手动释放
持久化内存不会随请求结束自动释放,如果分配后没有对应的释放逻辑,会导致内存持续增长。
规避方法:为持久化内存设计明确的生命周期,在扩展卸载或者对应数据不再使用时,主动调用pefree释放。
内存管理调试工具
开发PHP扩展时,可以开启Zend的内存调试模式,帮助定位内存泄漏问题。在编译PHP时加上--enable-debug参数,运行扩展时可以输出内存分配的详细信息,快速定位未释放的内存块。
另外也可以使用Valgrind工具检测扩展的内存问题,不过需要注意过滤PHP自身的内存分配噪音,重点看扩展代码中分配的内存是否正常释放。
PHP扩展内存管理Zend_Memory引用计数内存泄漏修改时间:2026-06-23 01:15:35