Pybind11存储numpy数组指针调用时指针失效崩溃问题求助
问题原因
崩溃的核心原因是你存储的numpy数组指针对应的内存被提前释放,具体有两个诱因:
- 测试代码中传入
init_ptrs的第二个参数torchTensorB.numpy() * 3840是临时生成的numpy数组,且如果数组不满足C风格连续的要求,pybind11会隐式创建临时数组副本完成类型转换,py::keep_alive无法正确延长这类隐式生成的临时对象的生命周期,函数调用结束后临时数组被销毁,m_ptrB直接变成悬空指针。 - C++类只存储了裸指针,没有持有对应numpy数组的引用,完全依赖
py::keep_alive保证内存有效性,本身稳定性极低,很容易出现指针悬空问题。
解决方案
方案一(推荐,最稳妥)
修改C++类,直接持有py::array_t作为成员变量,自动管理数组生命周期,不需要依赖py::keep_alive:
#include <pybind11/pybind11.h> #include <pybind11/numpy.h> #include <iostream> namespace py = pybind11; class Foo { public: void initPtrs( py::array_t<int32_t, py::array::c_style> pyAryA, py::array_t<float32_t, py::array::c_style> pyAryB ) { // 保存py::array实例,自动增加引用计数,保证内存不会被提前释放 m_aryA = pyAryA; m_aryB = pyAryB; m_ptrA = static_cast<int32_t*>(m_aryA.request().ptr); m_ptrB = static_cast<float32_t*>(m_aryB.request().ptr); } void doSomethingWithPtrs() { std::cout << m_ptrB[0] << std::endl; } private: py::array_t<int32_t, py::array::c_style> m_aryA; py::array_t<float32_t, py::array::c_style> m_aryB; int32_t* m_ptrA = nullptr; float32_t* m_ptrB = nullptr; };
绑定代码可以直接移除py::keep_alive配置:
PYBIND11_MODULE(bar,m) { py::class_<Foo>(m,"Foo") .def(py::init<>()) .def( "init_ptrs", &Foo::initPtrs ) .def( "do_something_with_ptrs", &Foo::doSomethingWithPtrs ); }
方案二(不修改C++类结构)
如果不能修改原有C++类,需要从Python调用侧做兼容:
- 不要直接传入临时数组,先把运算结果存到Python变量持有引用,同时显式转换为C风格连续数组,避免pybind11生成隐式临时副本:
def test( aryA, torchTensorB ): my_foo = bar.Foo() # 显式存为变量 + 转C风格连续数组 aryB = (torchTensorB.numpy() * 3840).copy(order='C') my_foo.init_ptrs( aryA, aryB ) my_foo.do_something_with_ptrs() # 注意:my_foo销毁前,aryA、aryB不能被Python回收
- 你原本的
py::keep_alive用法是正确的,py::keep_alive<1,2>表示只要Foo实例(参数1)存活,传入的第二个参数就不会被回收,符合需求。
内容的提问来源于stack exchange,提问作者user16372530
相关产品推荐
相关产品推荐

