Bullet3d 3.17库内崩溃排查请求:程序偶发退出码139
问题分析与解决:Bullet3D 3.17偶发崩溃(退出码139)
崩溃信息
程序偶发崩溃,退出码139(对应SIGSEGV段错误),堆栈跟踪如下:
#0 0x0000559e3a3e96f2 in removeleaf(btDbvt*, btDbvtNode*) () #1 0x0000559e3a3ebfc6 in btDbvt::update(btDbvtNode*, btDbvtAabbMm&) () #2 0x0000559e3a3cb303 in btCompoundShape::updateChildTransform(int, btTransform const&, bool) () #3 0x0000559e3a3cb45e in btCompoundShape::setLocalScaling(btVector3 const&) () #4 0x0000559e3a3cb3ff in btCompoundShape::setLocalScaling(btVector3 const&) ()
相关代码
Asset::ptr_t asset = Asset::loadAsset("box.GLB"); btCollisionShape_ptr shape = asset->createCollisionShape(); shape->setLocalScaling({1, 1, 1}); std::shared_ptr<btCollisionShape> Asset::createCollisionShape() { std::shared_ptr<Asset> self = shared_from_this(); ASSERT(self, "Failed to get self shared? possible constructed via new?"); class LocalShape { public: LocalShape(std::shared_ptr<Asset> parent) : parent_(parent) { } std::shared_ptr<Asset> parent_; btCompoundShape shape_; }; auto proxy = std::make_shared<LocalShape>(self); proxy->shape_.addChildShape(btTransform::getIdentity(), collisionShape_.get()); return std::shared_ptr<btCollisionShape>(proxy, &(proxy->shape_)); }
核心原因
问题出在shared_ptr的别名构造用法上:
- 代码中返回的
std::shared_ptr<btCollisionShape>是通过proxy(shared_ptr<LocalShape>)别名构造的,它共享proxy的引用计数,但指向的是LocalShape内部的shape_成员。 - 当所有持有该
btCollisionShape指针的shared_ptr销毁时,proxy的引用计数归零,LocalShape对象被销毁,其内部的btCompoundShape也随之被释放。 - 如果此时Bullet物理世界仍在引用这个已释放的
btCompoundShape(比如后续的物理模拟、形状更新操作),就会触发段错误,这也是崩溃偶发的原因——取决于shared_ptr的销毁时机与Bullet操作的时序。
修复方案
方案1:调整LocalShape的继承关系,直接管理btCompoundShape生命周期
让LocalShape继承自btCompoundShape,这样shared_ptr可以直接管理碰撞形状的内存,避免成员变量被提前释放:
std::shared_ptr<btCollisionShape> Asset::createCollisionShape() { std::shared_ptr<Asset> self = shared_from_this(); ASSERT(self, "Failed to get self shared? possible constructed via new?"); class LocalShape : public btCompoundShape { public: LocalShape(std::shared_ptr<Asset> parent) : parent_(parent) { } std::shared_ptr<Asset> parent_; }; auto proxy = std::make_shared<LocalShape>(self); proxy->addChildShape(btTransform::getIdentity(), collisionShape_.get()); return proxy; }
这种方式下,shared_ptr直接持有LocalShape(即btCompoundShape)的实例,只有当所有引用销毁时才会释放内存,确保Bullet操作期间形状始终有效。
方案2:额外检查子形状生命周期
确认collisionShape_(btCompoundShape的子形状)在btCompoundShape的整个使用周期内都有效。由于代码中parent_持有Asset的引用,只要LocalShape存在,Asset就不会被销毁,这部分无需额外调整,但需确保collisionShape_不会被Asset提前释放。
方案3:排查多线程访问问题
如果程序涉及多线程,需确保btCompoundShape的操作(如setLocalScaling)是单线程执行的,Bullet的碰撞形状不是线程安全的,多线程并发操作会导致未定义行为。
内容的提问来源于stack exchange,提问作者Max
相关产品推荐
相关产品推荐

