如何增加 ruby​​ 应用程序的堆栈大小。递归应用程序获取:堆栈级别太深(SystemStackError)

StackOverflow https://stackoverflow.com/questions/242617

  •  04-07-2019
  •  | 
  •  

在 stackoverflow.com 上发布堆栈溢出问题,多么有趣:-)

我正在运行一些递归 Ruby 代码,我得到: "Stack level too deep (SystemStackError)"

(我很确定代码是有效的,我没有陷入无限递归的死亡螺旋,但这不是重点)

有没有办法更改我的 Ruby 应用程序允许的堆栈深度/大小?

我不太明白这是否是 Ruby 中的限制,因为错误显示“堆栈级别”,这给我的印象是 Ruby 以某种方式计算堆栈的“级别”,或者它只是意味着堆栈已满。

我尝试在 Vista 和 Ubuntu 下运行该程序,结果相同。在 Ubuntu 下,我尝试使用“ulimit -s”将堆栈大小从 8192 更改为 16000,但这并没有改变任何内容。

编辑:感谢您的反馈。
我确实意识到使用递归函数可能不是最可靠的方法。但这也不是重点。我只是想知道是否有办法增加堆栈大小。时期。正如我提到的,我确实尝试在运行 ruby​​ 脚本之前运行 ulimit -s 16000 。没有任何改善..难道是我用错了?

编辑2:事实上,我在代码的边缘情况下有无限递归。
当你得到时截断的红宝石堆栈跟踪 "Stack level too deep" 错误有点误导。
当涉及多个函数的递归行为时,您会得到递归次数比实际次数少得多的印象。在此示例中,人们可能会认为它在 190 次以上调用后崩溃,但实际上大约是 15000 次调用

tst.rb:8:in `p': stack level too deep (SystemStackError)
        from tst.rb:8:in `bar'
        from tst.rb:12:in `bar'
        from tst.rb:19:in `foo'
        from tst.rb:10:in `bar'
        from tst.rb:19:in `foo'
        from tst.rb:10:in `bar'
        from tst.rb:19:in `foo'
        from tst.rb:10:in `bar'
         ... 190 levels...
        from tst.rb:19:in `foo'
        from tst.rb:10:in `bar'
        from tst.rb:19:in `foo'
        from tst.rb:22

-安德烈亚斯

有帮助吗?

解决方案

Ruby使用C堆栈,因此您的选项包括使用ulimit或使用某些编译器/链接器堆栈大小标志编译Ruby。尾递归尚未实现,Ruby目前对递归的支持并不是那么好。作为酷炫优雅的递归,您可能需要考虑应对语言的限制并以不同的方式编写代码。

其他提示

这个问题及其答案似乎可以追溯到 Ruby 1.8.x,它使用 C 堆栈。Ruby 1.9.x 及更高版本使用具有自己的堆栈的 VM。在 Ruby 2.0.0 及更高版本中,VM 堆栈的大小可以通过 RUBY_THREAD_VM_STACK_SIZE 环境变量。

如果您确定没有无限递归情况,那么您的algorythm可能不适合Ruby以一种重复的方式执行它。将algorythm从递归转换为不同类型的堆栈非常简单,我建议你尝试一下。这是你如何做到的。

def recursive(params)
  if some_conditions(params)
     recursive(update_params(params))
  end
end

recursive(starting_params)

将转变为

stack = [starting_params]
while !stack.empty?
  current_params = stack.delete_at(0)
  if some_conditions(current_params)
    stack << update_params(current_params)
  end
end

Yukihiro Matsumoto撰写此处

  

Ruby使用C堆栈,因此您需要   使用ulimit指定限制   堆栈深度。

想一想代码是怎么回事。正如其他海报所提到的那样,有可能破解解释器的C代码。然而。结果将是你使用更多的RAM并且不能保证你不会再次烧掉堆栈。

真正好的解决方案是为你想要做的事情提出一个迭代算法。有时候memoisation会有所帮助,有时你发现你没有使用你正在推送堆栈的东西,在这种情况下你可以用可变状态替换递归调用。

如果您是这类新手的新手,请查看SICP 此处了解一些想法...

只是遇到了同样的问题,很容易在Linux或Mac上修复。正如其他答案所述,Ruby使用系统堆栈设置。您可以通过设置堆栈大小在Mac和Linux上轻松更改此设置。福克斯的例子:

ulimit -s 20000

Ruby 1.9.2 开始,您可以打开尾调用优化,例如:

RubyVM::InstructionSequence.compile_option = {
  tailcall_optimization: true,
  trace_instruction: false
}

RubyVM::InstructionSequence.new(<<-EOF).eval
  def me_myself_and_i
    me_myself_and_i
  end
EOF
me_myself_and_i # Infinite loop, not stack overflow

如果递归调用位于方法的末尾且只是方法,那么将避免 SystemStackError 错误。当然,这个例子会导致无限循环。在进行深度递归之前,可能最好使用浅递归(并且没有优化)进行调试。

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top