应使用 memory_get_usage(true) 测量 ffi 调用前后内存差值以排查泄漏;ffi 不自动释放 c 层 malloc 内存,需手动 ffi::free(),尤其注意结构体中指针字段的显式释放。

用 memory_get_usage(true) 锁定 FFI 调用前后的内存差值
FFI 本身不自动管理 C 层分配的内存,但 PHP 层误用(比如反复 FFI::new() 却不 FFI::free())会导致脚本内存持续上涨。最直接的排查方式是在每次结构体传参调用前后打点:
- 必须加
true参数——memory_get_usage(true)返回的是真实分配的内存页大小,不受 Zend 内存池缓存干扰;只用memory_get_usage()容易误判 - 在循环中调用 C 函数时,每轮都记录差值:如果每轮增长稳定(如 +8KB),基本可断定结构体或其字段未被释放
- 特别注意
FFI::new('struct foo', true)中的$owned = true(默认值)——这意味着 PHP 会尝试在变量销毁时自动free(),但若结构体内部含指针(如char *指向 malloc 分配的内存),PHP 不会递归释放,必须手动处理
FFI::free() 必须显式调用,且不能依赖 GC 或作用域结束
PHP 的垃圾回收器对 FFI 分配的内存完全无感知,unset($ptr) 或函数返回后变量离开作用域,只会释放 PHP 的 FFI\CData 包装对象,底层 C 内存依然驻留。
- 所有通过
FFI::new(..., false)($owned = false)创建的结构体,必须配对调用FFI::free($ptr),否则 100% 泄漏 - 即使
$owned = true,若结构体中包含由 C 函数 malloc 分配并赋值给字段(如$s->buffer = some_c_malloc(1024);),也需在调用 C 函数释放逻辑后,手动FFI::free($s->buffer) - 不要把
FFI::free()放在__destruct()里——对象销毁时机不可控,且可能在请求结束前已被 GC 清理,导致free()调用失败或重复调用崩溃
检查 C 层是否 malloc 了结构体字段,且未暴露释放接口
很多 C 库在构造结构体时会内部 malloc 字段(如字符串、数组缓冲区),但只提供「使用」接口,不提供「释放」接口。PHP 层调用后,这些内存就彻底丢失控制权。
- 查看 C 头文件:搜索
malloc、calloc、strdup相关字段赋值逻辑,确认是否有配套的foo_free_struct()或类似清理函数 - 若 C 库无释放函数,只能改用
$owned = false+FFI::new()分配栈空间结构体,再用FFI::memcpy()把数据拷入,并确保所有指针字段指向 PHP 管理的内存(如FFI::string()返回的字符串底层是 PHP 的 zend_string) - 警惕
char *字段直接赋值 PHP 字符串——$s->name = 'hello';实际会触发隐式转换,把字符串内容复制进结构体内存,但 PHP 不知道该字段是否需要 free;更安全的做法是:$s->name = FFI::cast('char*', FFI::malloc(6)); FFI::memcpy($s->name, 'hello', 5);,后续再FFI::free($s->name)
避免在 CLI 循环中复用同一 FFI 实例做多次 FFI::new()
FFI 对象本身轻量,但频繁调用 FFI::new() 创建结构体实例,在 CLI 长运行脚本中极易累积泄漏,尤其当结构体含动态分配字段时。
- 不要写:
for ($i = 0; $i new('struct foo'); /* ... */ }—— 每次都新建,旧的没 free - 改用复用模式:
$s = $ffi->new('struct foo'); for (...) { /* 填充 $s 后调用 C 函数 */ $ffi->some_c_func($s); /* 若 $s 含 malloc 字段,此处应重置或 free 字段 */ } - 若必须批量创建,用数组收集所有
FFI\CData,循环结束后统一FFI::free();但要注意:PHP 数组本身也会占用内存,大量结构体下反而得不偿失
真正难排查的从来不是 FFI::new() 漏 free,而是 C 层悄悄 malloc 的字段——它不会出现在 PHP 的引用计数里,也不会被 debug_zval_dump() 显示,只能靠头文件+内存差值+手动审计交叉验证。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











