Java 为什么JVM还不支持尾调用优化?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/3616483/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-14 02:43:47  来源:igfitidea点击:

Why does the JVM still not support tail-call optimization?

javalanguage-agnosticoptimizationjvmtail-call-optimization

提问by soc

Two years after does-the-jvm-prevent-tail-call-optimizations, there seems to be a prototypeimplementationand MLVMhas listed the feature as "proto 80%" for some time now.

do-the-jvm-prevent-tail-call-optimizations两年后,似乎有一个原型实现MLVM将该功能列为“原型 80%”已经有一段时间了。

Is there no active interest from Sun's/Oracle's side in supporting tail calls or is it just that tail calls are "[...] fated to come in second place on every feature priority list[...]" as mentioned at the JVM Language Summit?

Sun/Oracle 方面是否对支持尾调用没有积极的兴趣,或者只是尾调用“[...]注定在每个功能优先级列表中排在第二位[...]”,如JVM 所述语言峰会

I would be really interested if someone has tested a MLVM build and could share some impressions of how well it works (if at all).

如果有人测试过 MLVM 构建并且可以分享它的工作效果(如果有的话),我会非常感兴趣。

Update:Note that some VMs like Aviansupport proper tail-calls without any issues.

更新:请注意,像Avian这样的一些 VM支持正确的尾调用,没有任何问题。

采纳答案by emory

Diagnosing Java Code: Improving the Performance of Your Java Code(alt) explains why the JVM does not support tail-call optimization.

诊断 Java 代码:提高 Java 代码的性能( alt) 解释了 JVM 不支持尾调用优化的原因。

But although it is well known how to automatically transform a tail-recursive function into a simple loop, the Java specification doesn't require that this transformation be made. Presumably, one reason it is not a requirement is that, in general, the transformation can't be made statically in an object-oriented language. Instead, the transformation from tail-recursive function to simple loop must be done dynamically by a JIT compiler.

但是,尽管众所周知如何将尾递归函数自动转换为简单循环,但 Java 规范并不要求进行这种转换。据推测,它不是必需的一个原因是,通常不能在面向对象的语言中静态地进行转换。相反,从尾递归函数到简单循环的转换必须由 JIT 编译器动态完成。

It then gives an example of Java code that won't transform.

然后给出了一个不会转换的 Java 代码示例。

So, as the example in Listing 3 shows, we cannot expect static compilers to perform transformation of tail recursion on Java code while preserving the semantics of the language. Instead, we must rely on dynamic compilation by the JIT. Depending on the JVM, the JIT may or may not do this.

因此,如清单 3 中的示例所示,我们不能指望静态编译器在保留语言语义的同时对 Java 代码执行尾递归转换。相反,我们必须依靠 JIT 的动态编译。根据 JVM,JIT 可能会也可能不会这样做。

Then it gives a test you can use to figure out if your JIT does this.

然后它提供了一个测试,您可以使用它来确定您的 JIT 是否执行此操作。

Naturally, since this is an IBM paper, it includes a plug:

自然,由于这是一篇 IBM 论文,它包含一个插件:

I ran this program with a couple of the Java SDKs, and the results were surprising. Running on Sun's Hotspot JVM for version 1.3 reveals that Hotspot doesn't perform the transformation. At default settings, the stack space is exhausted in less than a second on my machine. On the other hand, IBM's JVM for version 1.3 purrs along without a problem, indicating that it does transform the code in this way.

我用几个 Java SDK 运行了这个程序,结果令人惊讶。在 Sun 的 Hotspot JVM 版本 1.3 上运行表明 Hotspot 不执行转换。在默认设置下,我机器上的堆栈空间在不到一秒的时间内耗尽。另一方面,IBM 的 JVM 版本 1.3 没有问题,这表明它确实以这种方式转换了代码。

回答by oxbow_lakes

Java is the least functional language you could possibly imagine (well, OK, perhaps not!) but this would be a great advantage for JVM languages, like Scala, which are.

Java 是你能想象到的功能最少的语言(好吧,好吧,也许不是!)但这对于 JVM 语言来说是一个很大的优势,比如Scala

My observations are that making the JVM a platform for other languages has never seemed to be at the top of the priority list for Sun and I guess, now for Oracle.

我的观察是,让 JVM 成为其他语言的平台似乎从来都不是 Sun 的首要任务,我猜,现在对于 Oracle 来说。

回答by aioobe

Perhaps you know this already, but the feature is not as trivial as it may sound since the Java language actually exposes the stack trace to the programmer.

也许您已经知道这一点,但该功能并不像听起来那么简单,因为 Java 语言实际上向程序员公开了堆栈跟踪。

Consider the following program:

考虑以下程序:

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());
    }
}

Even though this has a "tail-call" it may not be optimized. (If it isoptimized, it still requires book-keeping of the entire call-stack since the semantics of the program relies on it.)

即使这有一个“尾调用”,它也可能没有被优化。(如果它优化,它仍然需要记录整个调用堆栈,因为程序的语义依赖于它。)

Basically, this means that it's hard to support this while still being backward compatible.

基本上,这意味着在仍然向后兼容的同时很难支持它。

回答by Alex Miller

One reason I've seen in the past for not implementing TCO (and it being seen as difficult) in Java is that the permission model in the JVM is stack-sensitive and thus tail-calls must handle the security aspects.

我在过去看到的没有在 Java 中实现 TCO(并且它被视为困难)的一个原因是 JVM 中的权限模型是堆栈敏感的,因此尾调用必须处理安全方面。

I believe this was shown to not be an obstacle by Clements and Felleisen [1] [2] and I'm pretty sure the MLVM patch mentioned in the question deals with it as well.

我相信 Clements 和 Felleisen [1] [2] 表明这不是障碍,我很确定问题中提到的 MLVM 补丁也能解决它。

I realize this does not answer your question; just adding interesting information.

我意识到这不能回答你的问题;只是添加有趣的信息。

  1. http://www.ccs.neu.edu/scheme/pubs/esop2003-cf.pdf
  2. http://www.ccs.neu.edu/scheme/pubs/cf-toplas04.pdf
  1. http://www.ccs.neu.edu/scheme/pubs/esop2003-cf.pdf
  2. http://www.ccs.neu.edu/scheme/pubs/cf-toplas04.pdf