为什么JVM仍然不支持tail-call优化?
两年之后,这个function似乎有了一个原型 实现 , MLVM已经把function列为“原型80%”了一段时间了。
Sun和Oracle在支持尾部呼叫方面没有积极的兴趣,或者只是尾部呼叫“ 在每个function优先级列表中被排在第二位 ”,正如JVM语言峰会 ?
如果有人testing了MLVM构build,并且可以分享一些它的工作效果(如果有的话),我会非常感兴趣。
更新: 请注意,像Avian这样的虚拟机支持正确的tail-calls,没有任何问题。
诊断Java代码:提高Java代码的性能 ( alt )解释了为什么JVM不支持尾部呼叫优化。
但是,尽pipe众所周知如何自动将尾recursion函数转换为简单循环,但Java规范并不要求进行这种转换。 据推测,这不是要求的一个原因是,一般来说,转换不能以面向对象的语言静态地进行。 相反,从尾recursion函数到简单循环的转换必须由JIT编译器dynamic完成。
然后给出了一个不会转换的Java代码的例子。
因此,如清单3中的示例所示,我们不能期望静态编译器在保留语言的语义的同时对Java代码执行尾recursion转换。 相反,我们必须依靠JIT的dynamic编译。 根据JVM的不同,JIT可能会也可能不会。
然后它给出了一个testing,你可以用来确定你的JIT是否这样做。
自然,由于这是一篇IBM论文,它包含一个插件:
我用两个Java SDK运行这个程序,结果令人惊讶。 在Sun的Hotspot JVM上运行版本1.3,表明Hotspot不执行转换。 在默认设置下,堆栈空间在我的机器上耗尽不到一秒钟。 另一方面,IBM的1.3版本的JVM毫无问题,表明它以这种方式转换代码。
我之前在Java中看到没有实现TCO(而且看起来很难)的一个原因是JVM中的许可模型是堆栈敏感的,因此tail-calls必须处理安全方面的问题。
我相信这不会成为Clements和Felleisen [1] [2]的障碍,我很确定问题中提到的MLVM补丁也是如此。
我意识到这不能回答你的问题。 只是添加有趣的信息。
也许你已经知道了,但是这个function并不像听起来那么简单,因为Java语言实际上把堆栈跟踪暴露给了程序员。
考虑下面的程序:
public class Test { public static String f() { String s = Math.random() > .5 ? f() : g(); return s; } public static String g() { if (Math.random() > .9) { StackTraceElement[] ste = new Throwable().getStackTrace(); return ste[ste.length / 2].getMethodName(); } return f(); } public static void main(String[] args) { System.out.println(f()); } }
即使这有一个“尾巴”,它可能不会被优化。 (如果它被优化,它仍然需要整个调用堆栈的logging,因为程序的语义依赖于它。)
基本上,这意味着在向下兼容的同时很难支持这一点。
Java是你可能想象得最less的function语言(嗯,好吧, 也许不是 !)但是这对于JVM语言来说是个很大的优势,比如说Scala 。
我的观察是,让JVM成为其他语言的平台似乎不在Sun的优先级列表的首位,我想,现在是Oracle。