在销毁期间使用std::shared_ptr发生段故障,可能是由于堆栈上的函数调用太多

Segfault using std::shared_ptr during destruction likely due to too many function calls on the stack

本文关键字:于堆栈 函数调用 太多 堆栈 故障 std shared 段故障 ptr      更新时间:2023-10-16

下面的代码可以正常编译和运行:

#include <memory>
struct MyTree {
    std::shared_ptr <MyTree> left;
    std::shared_ptr <MyTree> right;
    int val;
    MyTree(
        std::shared_ptr <MyTree> left_,
        std::shared_ptr <MyTree> right_,
        int val_
    ) : left(left_), right(right_), val(val_) {};
};
int main() {
    std::shared_ptr <MyTree> t(
        new MyTree( std::shared_ptr <MyTree>(),
                    std::shared_ptr <MyTree>(),
                    0)
    );  
    for(int i=0;i<10000;i++) {
        t.reset(new MyTree(t,t,0));
    }
}

但是,当for循环从10000更改为100000时,我收到一个段错误。看看gdb中的结果,看起来像std::shared_ptr中的垃圾收集导致的析构函数调用创建了数千个深度的回溯。因此,我认为段故障是由于从函数调用的堆栈上的空间耗尽。我有两个问题。首先,这是对分段断层的正确评估吗?其次,如果是这样,是否有一种好的方法来管理自定义数据结构,如需要垃圾收集的树,但可能非常大。谢谢。

这通常不是问题,因为通常你会保持树的平衡,并且深度是O(lgn)。

你得到了一个奇怪的单链表,每个指针都有一个副本。这是……奇怪。

一个真正的单链表将是非常深的递归,但可能受益于尾部调用优化,而不会耗尽堆栈。

你所遇到的问题是你混合了这两种数据结构所造成的。我看不出有什么好处

我看你的评估完全正确。看起来删除子树的递归调用超出了您的堆栈大小。这与shared_ptr无关,尽管我希望数据结构上的任何递归算法也以同样的方式失败。

如果可能的话,在你的平台上,处理这种大型结构的最简单的方法就是简单地增加堆栈的大小(例如ulimit),以允许自然递归算法发挥作用。

如果这是不可能的,你将不得不自己遍历节点,将遍历的结果存储到某种类型的容器中,这样你就可以切掉子节点,而不需要对树结构进行完整的深度遍历。

这看起来像是滥用std::shared_ptr。和一些非常糟糕的命名:您的类MyTree不是树,而是简单的一个节点。树应该是一个单独的类,并且应该删除析构函数中的所有节点。

已经说过,这不会改变太多关于手头有问题。你访问的是树的节点递归的(这是唯一有意义的方法),如果你如果树太深,栈就会溢出,不管怎样访问是否是隐式的(通过析构函数)std::shared_ptr)或显式调用。创建这样的树开始没有意义,因为创造没有意义在开始之前不能访问其节点的树破坏它。

编辑:

考虑评论中关于垃圾回收的讨论。使用Boehm收集器或其他垃圾收集器将解决释放元素的问题。但它仍然不允许你参观在解分配之前,这样的树仍然是无用的。我想那是在c++中支持垃圾回收是非常有力的论据,但事实并非如此其中之一)

我认为这种类型的错误可以没有shared_ptr复制。这基本上是一个长递归调用。好的,我只是创建并删除了树的一半但是。

struct MyTree {
    MyTree *left;
    int val;
    MyTree()
    {
      left = 0;
    }
    MyTree(MyTree *left_)
     : left(left_) {};
    ~MyTree()
    {
      delete left;
    }
};
int main() {
    MyTree *t = new MyTree();
    for(int i=0;i<100000;i++) {
      t = new MyTree(t);
    }
    delete t;
}

在100000之后再加一个零你会得到同样的错误信息