54

Java を高速にするためには JIT を使用する必要があると聞いたことがあります。これは、解釈と比較すると完全に理にかなっていますが、高速な Java コードを生成する事前コンパイラーを作成できないのはなぜでしょうか? については知ってgcjいますが、その出力は通常、たとえば Hotspot よりも速いとは思いません。

これを難しくしている言語について何かありますか? 私はそれがこれらのことだけに帰着すると思います:

  • 反射
  • クラスローディング

私は何が欠けていますか?これらの機能を回避した場合、Java コードを一度ネイティブ マシン コードにコンパイルするだけで完了できますか?

4

10 に答える 10

43

JIT コンパイラは、マシン コードが実行される正確なマシン上で生成されるため、高速になる可能性があります。これは、JIT が最適化されたコードを発行するために利用できる最良の情報を持っていることを意味します。

バイトコードをマシン コードにプリコンパイルすると、コンパイラはターゲット マシンに対して最適化できず、ビルド マシンに対してのみ最適化します。

于 2009-12-10T04:52:26.860 に答える
34

James Goslingによる Book Masterminds of Programmingの興味深い回答を貼り付けます。

ええと、Java の世界には実質的に 2 つのコンパイラーがあると言っているのを聞いたことがあります。Java バイトコードへのコンパイラーがあり、JIT があります。これは、基本的にすべてを具体的に再コンパイルします。恐ろしい最適化はすべて JIT にあります。

ジェームズ:その通りです。最近では、本当に優れた C および C++ コンパイラをほぼ常に打ち負かしています。動的コンパイラを使用すると、最後の瞬間にコンパイラが正しく実行されている場合に 2 つの利点が得られます。1 つは、実行しているチップセットを正確に把握していることです。多くの場合、人々が C コードの一部をコンパイルするとき、ある種の一般的な x86 アーキテクチャで実行するためにコンパイルする必要があります。入手したバイナリの中で、特によく調整されているものはほとんどありません。Mozilla の最新コピーをダウンロードすると、ほぼすべての Intel アーキテクチャ CPU で動作します。Linux バイナリはほぼ 1 つです。これは非常に汎用的で、あまり優れた C コンパイラではない GCC でコンパイルされています。

HotSpot が実行されると、実行中のチップセットが正確に認識されます。キャッシュがどのように機能するかを正確に知っています。メモリ階層がどのように機能するかを正確に認識しています。すべてのパイプライン インターロックが CPU でどのように機能するかを正確に認識しています。このチップが持っている命令セットの拡張機能を認識しています。使用しているマシンを正確に最適化します。残りの半分は、実行中のアプリケーションを実際に見ることです。何が重要かを知る統計を持つことができます。C コンパイラでは不可能なことをインライン化できます。Java の世界でインライン化される種類のものは、非常に驚​​くべきものです。次に、ストレージ管理が最新のガベージ コレクターと連携する方法に取り組みます。最新のガベージ コレクターを使用すると、ストレージの割り当てが非常に高速になります。

于 2011-04-10T06:06:29.140 に答える
25

The real killer for any AOT compiler is:

Class.forName(...)

This means that you cannot write a AOT compiler which covers ALL Java programs as there is information available only at runtime about the characteristics of the program. You can, however, do it on a subset of Java which is what I believe that gcj does.

Another typical example is the ability of a JIT to inline methods like getX() directly in the calling methods if it is found that it is safe to do so, and undoing it if appropriate, even if not explicitly helped by the programmer by telling that a method is final. The JIT can see that in the running program a given method is not overriden and is therefore in this instance can be treated as final. This might be different in the next invocation.


Edit 2019: Oracle has introduced GraalVM which allows AOT compilation on a subset of Java (a quite large one, but still a subset) with the primary requirement that all code is available at compile time. This allows for millisecond startup time of web containers.

于 2009-12-10T07:20:45.203 に答える
23

Java の JIT コンパイラも怠惰で適応性があります。

怠惰

怠惰なため、プログラム全体をコンパイルするのではなく、メソッドに到達したときにのみメソッドをコンパイルします (プログラムの一部を使用しない場合に非常に便利です)。クラスのロードは、まだ遭遇していないクラスを無視できるようにすることで、実際に JIT を高速化するのに役立ちます。

アダプティブ

適応型であるため、最初にマシンコードのクイックでダーティなバージョンを発行し、そのメソッドが頻繁に使用される場合にのみ戻ってスルージョブを実行します。

于 2009-12-10T04:59:21.083 に答える
12

最終的には、情報が多いほど最適化が向上するという事実に帰着します。この場合、JIT には、コードが実行されている実際のマシンに関するより多くの情報があり (Andrew が述べたように)、コンパイル中には利用できない多くのランタイム情報も含まれています。

于 2009-12-10T04:55:57.890 に答える
7

仮想メソッド境界を越えてインライン化し、効率的なインターフェイス ディスパッチを実行する Java の機能には、コンパイル前の実行時分析が必要です。つまり、JIT が必要です。すべてのメソッドが仮想であり、インターフェイスが「どこでも」使用されるため、大きな違いが生じます。

于 2009-12-10T04:54:50.100 に答える
7

理論的には、十分な時間と計算リソースが利用できる場合、JIT コンパイラーは AOT よりも有利です。たとえば、十分な RAM を備えたマルチプロセッサ サーバーでエンタープライズ アプリを数日から数か月実行している場合、JIT コンパイラはどの AOT コンパイラよりも優れたコードを生成できます。

現在、デスクトップ アプリを使用している場合、高速な起動や初期応答時間 (AOT が優れている) などの要素がより重要になります。また、高度な最適化を行うにはコンピューターに十分なリソースがない可能性があります。

また、リソースが不足している組み込みシステムを使用している場合、JIT は AOT に対抗するチャンスはありません。

ただし、上記はすべて理論でした。実際には、このような高度な JIT コンパイラを作成することは、まともな AOT コンパイラよりもはるかに複雑です。実用的な証拠はどうですか?

于 2009-12-10T06:02:22.727 に答える
6

JIT は、実行時にのみ知ることができるいくつかの条件を識別して排除できます。代表的な例は、最新の VM が使用する仮想呼び出しの排除です。たとえば、JVM がinvokevirtualorinvokeinterface命令を見つけた場合、呼び出されたメソッドをオーバーライドするクラスが 1 つしかロードされていない場合、VM は実際にその仮想呼び出しを静的にすることができるため、インライン化できます。それ。一方、C プログラムにとっては、関数ポインターは常に関数ポインターであり、その呼び出しをインライン化することはできません (一般的なケースではとにかく)。

JVM が仮想呼び出しをインライン化できる状況を次に示します。

interface I { 
    I INSTANCE = Boolean.getBoolean("someCondition")? new A() : new B();
    void doIt(); 
}
class A implements I { 
    void doIt(){ ... } 
}
class B implements I { 
    void doIt(){ ... } 
}
// later...
I.INSTANCE.doIt();

A他の場所でorBインスタンスを作成せず、それsomeConditionが に設定されていると仮定するとtrue、JVM は への呼び出しがdoIt()常に を意味することを認識しているA.doItため、メソッド テーブルのルックアップを回避し、呼び出しをインライン化できます。非 JITted 環境での同様の構成は、インライン化できません。

于 2009-12-10T07:45:36.073 に答える
2

公式の Java コンパイラが JIT コンパイラであるという事実は、これの大きな部分を占めていると思います。JVM と Java のマシン コード コンパイラの最適化にどれくらいの時間が費やされましたか?

于 2009-12-10T07:27:40.090 に答える