does-the-jvm-prevent-tail-call-optimizations后两年,似乎prototype implementation和MLVM已将该功能列为“proto 80%”一段时间了。
Sun的/ Oracle方面是否没有积极的兴趣来支持尾部调用,或者只是尾部调用“[...] 命中注定要在每个功能优先级列表中排在第二位 [ ......]“正如JVM Language Summit提到的那样?
如果有人测试了MLVM构建并且可以分享它的工作原理(如果有的话),我会非常感兴趣。
更新: 请注意,Avian等某些虚拟机支持正确的尾部调用,没有任何问题。
答案 0 :(得分:32)
Diagnosing Java Code: Improving the Performance of Your Java Code(alt)解释了为什么JVM不支持尾调用优化。
但是,尽管众所周知如何将尾递归函数自动转换为简单循环,但Java规范并不要求进行此转换。据推测,不是要求的一个原因是,通常,转换不能在面向对象的语言中静态地进行。相反,从尾递归函数到简单循环的转换必须由JIT编译器动态完成。
然后给出了一个不会转换的Java代码示例。
因此,正如清单3中的示例所示,我们不能指望静态编译器在保留语言语义的同时对Java代码执行尾递归转换。相反,我们必须依靠JIT的动态编译。根据JVM,JIT可能会也可能不会这样做。
然后它给出了一个测试,你可以用来判断你的JIT是否这样做。
当然,由于这是一篇IBM论文,它包含一个插件:
我用几个程序运行了这个程序 Java SDK,结果是 奇怪。在Sun的Hotspot上运行 版本1.3的JVM揭示了这一点 热点不执行 转型。在默认设置下, 堆栈空间耗尽 我的机器上只有一秒钟。在 另一方面,IBM的1.3版JVM without without without without,,, 表明它确实改变了 以这种方式编码。
答案 1 :(得分:30)
我过去看到的一个原因是,在Java中没有实现TCO(并且被认为是困难的)是JVM中的权限模型是堆栈敏感的,因此尾部调用必须处理安全方面。
我认为这显示不是Clements和Felleisen [1] [2]的障碍,而且我很确定问题中提到的MLVM补丁也会处理它。
我意识到这不能回答你的问题;只是添加有趣的信息。
答案 2 :(得分:14)
也许你已经知道了,但是这个功能并不像听起来那么简单,因为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());
}
}
即使这有“尾调用”,也可能无法优化。 (如果 进行了优化,它仍然需要整个调用堆栈的簿记,因为程序的语义依赖于它。)
基本上,这意味着在向后兼容时很难支持这一点。
答案 3 :(得分:12)
Java是你可以想象的功能最少的语言(好吧,好的,perhaps not!)但这对于JVM语言来说是一个很大的优势,比如Scala。
我的观察是,让JVM成为其他语言的平台似乎从未成为Sun优先级列表的首选,我想现在对于Oracle而言。
答案 4 :(得分:0)
这不是 Java 的问题……它是 JVM 的问题之一。 Java 只是 JVM 语言的盛大- ol'-pa。
制作 TCO 是在删除当前堆栈帧的同时跳转到下一个堆栈帧,在正在运行的程序和当前堆栈调用之间的变量应该在其他地方...;)
最好的方法是为其他帧中的跳转调用添加一个新的特殊调用操作码。他们已经为虚拟电话做到了这一点。真的不是解释上的问题,JIT 可能会引发其他问题,JVM 够臃肿了。
在Java或其他语言中,由于没有适当的TCO,另一种方式是蹦床,但它增加了大量代码。或者使用特定的异常,但它很混乱。它在您的代码中,但不在其他人的库中...
啊!如果 Rich Hickey 添加了一个(recur...)东西(它不是一个功能),那是因为缺乏真正的 TCO,他不希望人们认为有一个。他可以很容易地在内部尾呼中自动制定 TCO。它还有助于检测不在尾部位置的坏尾调用。
还有一个(trampoline...)用于外部 TCO 的东西,但它很乱(作为一个蹦床),并且除了在糟糕的堆栈情况外几乎不使用。
但是是的,很多虚拟机都在管理 TCO。我听说 CLR 会。我什至见过管理它的付费 JVM(前段时间,不记得了...)
js 中的蹦床示例:https://codeinjavascript.com/2020/06/13/tail-call-optimization-tco/
关于具有帧覆盖的 HotSpot VM 的 TCO 的旧论文:https://ssw.jku.at/Research/Papers/Schwaighofer09Master/schwaighofer09master.pdf