You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

PyPy环境下CFFI调用时bytes对象缓冲区重分配导致指针失效问题

问题成因与解答

1. 关于CFFI直接获取bytes底层缓冲区指针的假设是否成立

这个假设仅在CPython解释器下成立,在你使用的PyPy解释器下不成立:

  • CPython采用引用计数为主、非移动分代GC为辅的内存管理机制,只要Python侧持有bytes对象的引用,其底层缓冲区的内存地址就不会发生变化,CFFI传递bytes到char*类型参数时确实直接提取内部缓冲区指针,不会产生临时拷贝。
  • PyPy采用可移动的分代GC机制,会定期进行内存紧凑整理,移动堆上存活对象的位置以减少内存碎片。bytes对象的底层缓冲区存储在GC托管的堆上,GC移动对象时只会自动更新Python侧的引用地址,不会同步修改C层持有的裸指针,因此调用返回后保留的指针会直接失效。你观测到同一个bytes对象两次调用传入的指针地址不同,就是两次调用的间隔触发了GC内存整理,bytes缓冲区被移动到了新的内存地址。

2. Python是否允许静默重分配bytes底层缓冲区

  • CPython不会进行这类操作,只要bytes对象未被回收,底层缓冲区地址固定。
  • PyPy的移动GC设计本身就允许静默移动包括bytes在内的所有堆内对象,该过程不需要感知外部C代码是否持有其指针,PyPy默认约定FFI调用传递的堆内对象指针仅在本次调用执行期间有效,调用返回后继续持有属于未定义行为。

3. 长期持有C层指针的正确实现方式

如果需要原生代码长期持有数据指针,需要使用CFFI分配的非移动堆内存,而非Python原生的bytes对象,示例如下:

from cffi import FFI
ffi = FFI()
lib = ffi.dlopen(libname)
ffi.cdef("""
    void foo(char*);
""")
x = b'abc123'
# 用ffi.new分配非GC移动的内存,持有cdata_obj引用即可保证地址不变
cdata_obj = ffi.new("char[]", x)
lib.foo(cdata_obj)

只要Python侧一直持有cdata_obj的引用,其对应的内存地址就不会发生变化,原生代码保留的指针可以长期安全使用。


内容的提问来源于stack exchange,提问作者DeducibleSteak

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.24 02:06:03