web-dev-qa-db-ja.com

クラスをfinalとして定義すると、JVMのパフォーマンスが向上するのはなぜですか?

http://sites.google.com/site/gson/gson-design-document からの引用:

Gsonのほとんどのクラスが最終としてマークされているのはなぜですか?

Gsonは、プラグ可能なシリアライザーとデシリアライザーを提供することでかなり拡張可能なアーキテクチャを提供しますが、Gsonクラスは拡張可能になるように特別に設計されていません。非最終クラスを提供すると、ユーザーはGsonクラスを合法的に拡張し、その後のすべてのリビジョンでその動作が機能することを期待できます。クラスをfinalとしてマークし、拡張性を可能にするために適切なユースケースが出現するまで待つことで、このようなユースケースを制限することを選択しました。クラスファイナルをマークすることには、Javaコンパイラと仮想マシンに追加の最適化の機会を提供するという小さな利点もあります。

なぜそうなのですか? [私が推測すると、JVMはクラスが最終であることを知っているので、メソッドオーバーライドテーブルを維持しませんか?他に理由はありますか?]

パフォーマンスのメリットは何ですか?

これは、頻度がインスタンス化されるクラス(POJO?)に適用されますか、それとも静的メソッドを保持するクラス(ユーティリティクラス)に適用されますか?

最終として定義されたメソッドは、理論的にパフォーマンスを向上させることもできますか?

何か影響はありますか?

ありがとう、マキシム。

42
Maxim Veksler

仮想(オーバーライドされた)メソッドは通常、最終的に関数ポインターであるある種のテーブル(vtable)を介して実装されます。各メソッド呼び出しには、そのポインターを通過する必要があるというオーバーヘッドがあります。クラスがfinalとマークされると、すべてのメソッドをオーバーライドできなくなり、テーブルを使用する必要がなくなります。これにより、より高速になります。

一部のVM(HotSpotなど)は、よりインテリジェントに処理を実行し、メソッドがオーバーライドされるかどうかを認識し、必要に応じてより高速なコードを生成する場合があります。

ここ はHotSpotに関するより具体的な情報です。そして、いくつかの 一般 情報も。

39
TofuBeer

古い、明らかにもはやない しかし、それでも大部分は 関連する、IBMからのこれに関する記事 developerWorks 、次のように述べています。

一般的な認識は、クラスまたはメソッドをfinalと宣言すると、コンパイラがメソッド呼び出しをインライン化するのが容易になるというものですが、この認識は正しくありません(または、少なくとも非常に誇張されています)。

最終的なクラスとメソッドは、プログラミング時に重大な不便をもたらす可能性があります。これらは、既存のコードを再利用し、既存のクラスの機能を拡張するためのオプションを制限します。不変性を強制するなどの正当な理由でクラスがfinalになることもありますが、finalを使用する利点は不便さを上回るはずです。パフォーマンスの向上は、ほとんどの場合、優れたオブジェクト指向設計の原則を損なう悪い理由であり、パフォーマンスの向上が小さいか存在しない場合、これは確かに悪いトレードオフです。

こちらもご覧ください 別の質問に関連する回答。Netの同等の質問、ここで説明 もあります。 SOディスカッション、 " 最終的なメソッドはインライン化されています ?" " 明日はどの最適化が役に立たないでしょう "というタイトルの質問について、これはリストにが表示されます。

これが 効果を特徴付けようとしている誰か 静的クラスと最終クラスからインライン化するHotSpotです。

finalクラスとfinalメソッドの効果が絡み合っていることにも注意してください。あなたmayは、確かにfinalメソッドのパフォーマンス上の利点を得ることができます(ここでも、良いリファレンスはありません)可能性があるJITに、他の方法では実行できない(または単純ではない)インライン化を実行するように指示します。クラスfinalをマークしても同じ効果が得られます。つまり、すべてのメソッドも突然最終的になります。 Sun/Oracleの人々は、HotSpotは通常、finalキーワードの有無にかかわらずこれを実行できると主張していることに注意してください。クラス自体をfinalにすることによる追加の効果はありますか?

参考までに、 最終メソッド および 最終クラス のJLSへのリンク。

19
andersoj

特定のすべてのJVMの実装を知らないので、理論的には、オブジェクトへのポインターが最終的な型へのポインターであることをJVMが知っている場合、非仮想関数呼び出し(つまり、直接対間接)を実行できます。メンバー関数への変換(つまり、関数ポインターを介した間接参照なし)。これにより、実行が高速化される可能性があります。これはまた、ひいては潜在的な可能性につながる可能性があります。

1

クラスをfinalとしてマークすると、JITステージでさらに最適化を適用できます。

非最終クラスで仮想メソッドを呼び出す場合、適切な実装がそのクラスで定義されているものなのか、それとも知らないサブクラスなのかがわかりません。

ただし、最終クラスへの参照がある場合は、必要な特定の実装を知っています。

考えてみましょう:

A extends B
B extends C

B myInstance = null;
if(someCondition)
   myInstance = new B();
else
   myInstance = new C();
myInstance.toString();

この場合、JITは、CのtoString()の実装またはBのtoString()の実装が呼び出されるかどうかを知ることができません。ただし、Bが最終としてマークされている場合、B以外の実装が適切な実装になることは不可能です。

1
LorenVS

違いはありません、それは単なる憶測です。それが意味を持つ唯一の状況は、jvmがそれらを異なる方法で処理するStringなどのクラスです。

0
Stas