代码之家  ›  专栏  ›  技术社区  ›  hawkeye

为了实现尾调用优化,JVM必须牺牲什么?

  •  3
  • hawkeye  · 技术社区  · 15 年前

    人们说,Clojure的实现非常出色,除了没有尾调用优化的限制之外——这是对JVM的限制,而不是Clojure的实现。

    http://lambda-the-ultimate.org/node/2547

    据说在Python中实现TCO会牺牲

    • 堆栈跟踪转储,以及
    • 调试规律。

    Explain to me what the big deal with tail call optimization is and why Python needs it

    对于TCO的JVM实现,是否也必须做出同样的牺牲?还要牺牲什么吗?

    3 回复  |  直到 8 年前
        1
  •  6
  •   ShuggyCoUk    15 年前

    虽然不同(IL指令已经存在),但值得注意的是.NET 64 bit JIT team 必须通过检查才能尊重所有的追尾电话。

    我特别提出了以下意见:

    当然,缺点是,如果您必须调试或分析优化的代码,那么就要准备好处理那些看起来像是丢失了几帧的调用堆栈。

    我认为JVM也不太可能避免这种情况。

    鉴于此,在请求尾调用优化的情况下,JIT应该假定 必修的 为了避免堆栈溢出,这不是可以在调试版本中关闭的东西。如果它们在您进入感兴趣的部分之前崩溃,那么它们对调试没有太大的用处。“优化”实际上是一个永久性的特性,也是受其影响的堆栈跟踪的一个问题。

    值得指出的是,在执行程序员概念上描述/理解为堆栈操作(例如调用函数)的操作时,任何避免创建实际堆栈帧的优化都会导致在调试/提供堆栈跟踪和rea时向用户呈现的内容之间的断开。能力。
    这是不可避免的,因为描述操作的代码越来越远离执行操作的状态机的机制。

        2
  •  2
  •   John M    15 年前

    工作是 underway now 向JVM添加尾部调用。有一个 wiki page 谈论一些细节。

        3
  •  0
  •   mikera    14 年前

    是的,通常情况下,实现TCO会阻止您获得完整的堆栈跟踪。这是不可避免的,因为总体拥有成本的关键是避免创建额外的堆栈帧。

    值得注意的是,Clojure有一个不消耗堆栈的“recur”特性来绕过当前JVM版本上的这个约束。

    例子:

    (defn triangle [n accumulator] 
      (if 
        (<= n 0)  
          accumulator
          (recur (dec n) (+ n accumulator))))
    
    (triangle 1000000 0)
    
    => 500000500000     (note stack does not explode here!)