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

问题描述 投票:0回答:7

在 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"
错误时,截断的 ruby 堆栈跟踪有点误导。
当涉及多个函数的递归行为时,您会得到递归次数比实际次数少得多的印象。在这个例子中,人们可能会认为它在 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 recursion stack-overflow
7个回答
29
投票

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

RUBY_THREAD_VM_STACK_SIZE
环境变量控制 VM 堆栈的大小。


13
投票

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

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

9
投票

松本幸弘写于这里

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


7
投票

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


3
投票

想想代码发生了什么。正如其他发帖者所提到的,破解解释器的 C 代码是可能的。然而。结果将是您使用更多的 RAM 并且不能保证您不会再次破坏堆栈。

真正好的解决方案是为您想要做的事情提出一个迭代算法。有时记忆会有所帮助,有时您会发现自己没有使用推入堆栈的内容,在这种情况下,您可以用可变状态替换递归调用。

如果您对此类内容不熟悉,请查看 SICP here 获取一些想法...


3
投票

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

ulimit -s 20000

2
投票

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错误。当然,这个例子将导致无限循环。在进行深度递归之前,最好使用浅层递归(并且不进行优化)进行调试。

© www.soinside.com 2019 - 2024. All rights reserved.