Java 中的尾调用优化

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

从 Java 8 开始,Java 不提供尾部调用优化 (TCO)。 经过研究,我知道了原因,即:

在 JDK 类中 [...] 有许多安全敏感方法,这些方法依赖于计算 JDK 库代码和调用代码之间的堆栈帧来确定谁在调用它们。

然而基于 JVM 的 Scala 支持 Tail-Call 优化。 Scala 在编译时进行尾递归优化。为什么 Java 不能使用同样的方法?

PS:不确定Java的最新版本(Java 11)现在是否有TCO。如果有知道的朋友也可以分享一下就太好了

备注:

  1. 我知道 TCO 处于积压状态并且优先级较低,但想知道为什么 Java 不能像 Scala 那样在编译时进行更改。

  2. Java 没有尾部调用优化,这与大多数命令式语言没有尾部调用优化的原因相同。命令式循环是该语言的首选样式,程序员可以用命令式循环替换尾递归。 (来源

java recursion compilation jvm tail-recursion
3个回答
27
投票

为什么 Java 不能使用同样的方法?

我不能说将使用哪种方法,但在Project Loom的提案中有更好的解释:

毫无疑问,需要向 JVM 添加操作调用堆栈的功能,因此该项目的目标是添加一个更轻量级的构造,该构造将允许将堆栈展开到某个点,然后使用给定参数调用方法(基本上,高效尾部调用的概括)。我们将该功能称为“展开并调用”或 UAI。向 JVM 添加自动尾部调用优化并不是该项目的目标。

据我所知,尾部调用的工作尚未开始,因为 Fibers 和 Continuations 目前似乎具有更高的优先级。


18
投票

我在这里阅读了一篇非常好的博客文章,介绍了如何在 Java 中实现尾递归: Knoldus 关于 Java 尾递归的博客文章

但是,他们博客上的代码无法编译,因此我用他们的代码创建了一个小型存储库,但修复了语法以便可以编译。 包含工作代码的 Github 存储库

希望这对某人有用,我发现 Knoldus 博客文章中提出的想法非常有趣。

编辑:实际上我后来发现博客文章中提出的想法最初是 Venkat Subramaniam 的。他在演讲中讨论了这些主题这里


0
投票

IBM J9 一直都有尾调用优化,并且它是经过认证的 Java VM。因此,Java 规范中没有任何内容阻止 OpenJDK 作者进行这种特定的优化。它可以通过多种方式实施。例如在 JDK 级别:作为蹦床,或作为重用堆栈框架。或者在编译时,理论上 javac 可以检测递归并将其重写为循环或蹦床。

反对尾部调用优化的最常见原因是它创建了“谎言”堆栈跟踪 - 这个原因可能是真的,但在我看来并不是特别有力。

我的猜测是,没有人认为这种优化足够重要而费心去实施它。尾递归不是解决性能问题的典型 Java 方法。性能重要与否。如果不重要就不需要优化,如果重要就重写为迭代。

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