web-dev-qa-db-ja.com

Java末尾再帰の最適化がまったくないのはなぜですか?

私が読んだことから:継承があるため、実際に呼び出されるメソッドを判別するのは容易ではないためです。

しかし、なぜJavaが静的メソッドの末尾再帰最適化を持たず、コンパイラで静的メソッドを呼び出す適切な方法を強制しないのですか?

Javaが末尾再帰をまったくサポートしないのはなぜですか?

ここで何か困難があるかどうかはわかりません。


JörgW Mittag で説明されている 推奨される重複 について1

  • もう1つの質問はTCOについてですが、これはTREについてです。 TREはTCOよりもはるかに単純です。
  • また、他の質問では、JVMがJVMにコンパイルする言語実装に課す制限について質問します。この質問では、Javaについて質問します。Javaは、JVMによって制限されない1つの言語です。 Javaを設計する人たちと同じです。
  • 最後に、JVMにはTREに関する制限さえありません。JVMにはメソッド内GOTOがあり、TREに必要なのはそれだけです。

1作成されたポイントを呼び出すためにフォーマットが追加されました。

97
InformedA

Brian Goetz(OracleのJava言語アーキテクト)がこれについて説明したように video

jdkクラス[...]には、jdkライブラリコードと呼び出しコードの間のスタックフレームのカウントに依存して、誰が呼び出しているかを特定する、セキュリティ上重要なメソッドがいくつかあります。

スタック上のフレーム数を変更すると、これが壊れ、エラーが発生します。これは愚かな理由だったと彼は認め、JDKの開発者たちはその後このメカニズムを置き換えました。

彼はさらに、それは優先事項ではなく、その末尾再帰であることを言及します

最終的に行われます。

N.B.これはHotSpotとOpenJDKに適用され、他のVMは異なる場合があります。

138
ggovan

ほとんどの命令型言語にはないのと同じ理由で、Javaにはテールコール最適化がありません。命令型ループは言語の優先スタイルであり、プログラマーは末尾再帰を命令型ループに置き換えることができます。スタイルの問題として使用が推奨されていない機能では、複雑さは価値がありません。

プログラマーがFPスタイルで他の方法で命令する言語で書くことを望むこのことは、コンピューターがGHzではなくコアでスケーリングし始めた後、過去10年ほどで流行しました。今でもそれほど人気はありません。命令型ループを作業中に末尾再帰で置き換えることを提案した場合、コードレビューアの半分は笑い、残りの半分は混乱した見た目になります。関数型プログラミングであっても、高次関数のような他の構造がうまく適合しない場合を除いて、通常、テール再帰を回避します。

24
Karl Bielefeldt

JVMにはテールコール用のバイトコードがないため、Javaにはトールコール最適化がありません(一部の静的に不明関数ポインター、たとえば一部のvtableのメソッド)。

社会的(そしておそらく技術的)な理由から、JVMに新しいバイトコード操作を追加すると(そのJVMの以前のバージョンとの互換性が失われます)、JVM仕様の所有者にとって非常に困難です。

JVM仕様に新しいバイトコードを追加しないことの技術的な理由には、実際のJVM実装が非常に複雑なソフトウェアであるという事実が含まれます(たとえば、JITの最適化が多く行われているため)。

不明な関数の末尾呼び出しでは、現在のスタックフレームを新しいものに置き換える必要があり、その操作はJVM内に配置する必要があります(バイトコード生成コンパイラを変更するだけの問題ではありません)。

言語に末尾呼び出し(再帰的またはそれ以外)を行うための特別な構文がなく、コンパイラが末尾呼び出しが要求されても生成できない場合を除き、「オプション」の末尾呼び出しまたは末尾再帰の最適化により、 1つのマシンでは100バイト未満のスタックが必要ですが、別のマシンでは100,000,000バイトを超えるコードが必要になる場合があります。このような違いは、単なる量的なものではなく、質的なものと見なされるべきです。

マシンのスタックサイズが異なることが予想されるため、あるマシンではコードが機能し、別のマシンではスタックがブローする可能性が常にあります。ただし、一般に、スタックが人工的に制限されている場合でも1台のマシンで動作するコードは、「通常の」スタックサイズのすべてのマシンで動作する可能性があります。ただし、1,000,000の深さを再帰するメソッドが、あるマシンでテールコール最適化され、別のマシンでは最適化されない場合、前のマシンでの実行は、スタックが異常に小さい場合でも機能し、スタックが異常に大きい場合でも失敗する可能性があります。 。

4
supercat

Javaでテールコール再帰が使用されていないのは、主にスタックトレースが変更され、プログラムのデバッグが非常に困難になるためです。Javaの主な目的の1つは、プログラマーに許可することです。特に、高度にオブジェクト指向のプログラミング環境でそれを行うには、スタックトレースが不可欠です。反復を代わりに使用できるため、言語委員会は末尾再帰を追加する価値がないと考えているはずです。

2
annoying_squid