通过在堆上分配堆栈部分来避免堆栈溢出?

有没有一种语言,我们可以启用一个机制,当超过原始堆栈空间时,在堆上分配新的堆栈空间?

我记得在我的大学做了一个实验室,在那里我们用C中的内联汇编来实现一个基于堆的可扩展堆栈,所以我原则上知道它应该是可能的。

我知道在开发应用程序时得到堆栈溢出错误可能很有用,因为它会在不使系统占用大量内存并开始交换的情况下快速终止疯狂的无限递归。

但是,如果您已经完成了经过良好测试的应用程序,并且希望它尽可能健壮(例如,它是在台式计算机上运行的非常关键的程序),那么很高兴知道它不会在堆栈更有限的某些其他系统上,一些对象需要更多空间,或者如果程序面临的特殊情况需要比测试中更多的堆栈内存,那么可悲的是失败。

我认为这是因为这些缺陷通常会在生产代码中避免递归。 但是如果我们有一个在生产代码中自动堆栈扩展的机制,我们可以使用递归编写更优雅的程序,因为知道它不会意外地发生段错误,而系统有16 GB的堆内存可以使用。


它有先例。

  • GHC(Haskell编译器)的运行时使用堆而不是堆栈。 只有当您调用外部代码时才使用堆栈。

  • Google的Go实现为goroutine使用分段堆栈,根据需要放大堆栈。

  • Mozilla的Rust过去使用分段堆栈,尽管它决定引起更多问题而不是解决问题(参见[rust-dev]放弃Rust中的分段堆栈)。

  • 如果内存服务,一些Scheme实现将堆栈帧放在堆上,然后垃圾像其他对象一样收集帧。

  • 在命令式语言的传统编程风格中,大多数代码将避免递归调用自身。 堆栈溢出在野外很少见到,它们通常是由马虎编程或恶意输入触发的 - 尤其是递归下降解析器等,这就是为什么一些解析器在嵌套超过阈值时拒绝代码。

    避免生产代码中堆栈溢出的传统建议:

  • 不要写递归代码。 (例如:重写搜索算法以使用显式堆栈。)

  • 如果你写了递归代码,证明递归是有界的。 (例如:搜索平衡树受树大小的对数限制。)

  • 如果你无法证明它是无界的,那么给它添加一个界限。 (例如:为解析器支持的嵌套量添加限制。)


  • 我不相信你会找到一种强制性的语言。 但是一个特定的实现可以提供这样的机制,并且根据操作系统的不同,运行时环境可以根据需要自动扩展堆栈。


    根据gcc的文档,如果使用-fsplit_stack选项进行编译, gcc可以生成这样的代码:

    -fsplit-stack
         Generate code to automatically split the stack before it overflows.
         The resulting program has a discontiguous stack which can only
         overflow if the program is unable to allocate any more memory.
         This is most useful when running threaded programs, as it is no
         longer necessary to calculate a good stack size to use for each
         thread.  This is currently only implemented for the i386 and
         x86_64 backends running GNU/Linux.
    
         When code compiled with -fsplit-stack calls code compiled
         without -fsplit-stack, there may not be much stack space
         available for the latter code to run.  If compiling all code,
         including library code, with -fsplit-stack is not an option,
         then the linker can fix up these calls so that the code compiled
         without -fsplit-stack always has a large stack.  Support for
         this is implemented in the gold linker in GNU binutils release 2.21
         and later.
    

    llvm代码生成框架提供了对分段堆栈的支持,这些堆栈在go语言中使用,最初用于Mozilla的rust (尽管它们已从防火墙中移除,理由是执行开销对于“高性能语言”来说太高, 。(看到这个邮件列表线程)

    尽管存在rust -team的反对意见,但分段堆栈的速度惊人地快,尽管堆栈抖动问题会影响特定的程序。 (有些Go程序也会遇到这个问题。)

    Henry Baker在其1994年的Cheney论文中提出了一种以相对有效的方式堆分配堆栈段的另一种机制,并成为Chicken Scheme的运行时间的基础,这是一种主要与R5RS兼容的编译方案实现。

    链接地址: http://www.djcxy.com/p/14123.html

    上一篇: Avoiding stack overflows by allocating stack parts on the heap?

    下一篇: Why and how do people implement their own malloc?