Java AOTコンパイラはどのように機能しますか?


18

Javaアプリをネイティブ実行可能ファイルに変換することを主張するツール(Excelsior JETなど)がいくつかあります(*.exe)。ただし、これらのツールは実際にはシェルまたはコマンドラインから起動/実行するネイティブラッパーを作成しているにすぎないことを理解していますjava

その理解が間違っている場合、それがどうなるかわかりません。実行中のJVM(javaプロセス)が本質的に高性能インタープリターであり、Javaクラスファイルからバイトコードをオンザフライでロードする場合、Javaアプリ(JVMへの入力として機能するバイトコードファイルのコレクション)がどのようになるかわかりません本当に実行可能ファイルに変換されます。

これは、JVMプロセスがすでにバイトコードファイルのセットを入力として受け取るネイティブ実行可能ファイルであるためです。これらのバイトコードファイルとJVMプロセスを単一の統合されたネイティブ実行可能ファイルにマージすることは、JVMを完全に書き直し、JVM仕様から脱線しなければ不可能だと思われます。

だから私は尋ねる:これらのツールは実際に Javaクラスファイルをネイティブの実行可能ファイルにどのよう「変換」するのか?

回答:


26

すべてのプログラムにはランタイム環境があります。私たちはこれを忘れがちですが、そこにあります。オペレーティングシステムへのシステムコールをラップするC用の標準ライブラリ。Objective-Cには、すべてのメッセージパッシングをラップするランタイムがあります。

Javaの場合、ランタイムはJVMです。よく知られているJava実装のほとんどは、バイトコードインタープリターおよびJITコンパイラーであるHotSpot JVMに似ています。

これが唯一の実装である必要はありません。Java用の標準のlib-esqueランタイムをビルドしてコードをネイティブマシンコードにコンパイルし、新しいオブジェクトの呼び出しをmallocに処理し、ファイルアクセスをマシンのシステムコールに処理するランタイム内で実行できないと言うことはまったくありません。そして、それはAhead Of Time(JITではなくAOT)コンパイラーが行うことです。あなたは...あなたはJVMの実装(と、それはそれを呼び出すことができますランタイムどんなことをコール JVMの仕様に従う)またはJavaのランタイム環境または標準のlib。そことそれは本質的に同じことをします。

それはjavac、ネイティブマシンをターゲットとするように再実装することで実行できます(GCJが行ったようなことです)。または、によって生成さjavacれたバイトコードを別のマシンのマシン(またはバイト)コードに変換することで実行できます。これがAndroidの機能です。Wikipediaに基づいて、Excelsior JETも同様です(「コンパイラは、ポータブルJavaバイトコードを目的のハードウェアとオペレーティングシステム(OS)向けに最適化された実行可能ファイルに変換します」)。これはRoboVMにも当てはまります。

Javaにはさらに複雑な問題があります。これは、排他的なアプローチとしてこれを行うのが非常に難しいことを意味します。クラスの動的ロード(class.forName())またはプロキシされたオブジェクトには、AOTコンパイラが簡単に提供できないダイナミクスが必要であるため、それぞれのJVMには、プリコンパイルできないクラスを処理するためのJITコンパイラ(Excelsior JET)またはインタープリタ(GCJ)も含まれている必要がありますネイティブ。

覚えておいて、JVMが仕様で多くの実装で。C標準ライブラリは、さまざまな実装を備えた仕様もあります。

Java8では、AOTコンパイルに関してかなりの作業が行われました。せいぜい、テキストボックスの範囲内でのみ一般的にAOTを要約できます。ただし、2015年のJVM Language Summit(2015年8月)では、Java Goes AOT(youtube video)というプレゼンテーションがありました。このビデオの長さは40分で、技術的な側面とパフォーマンスのベンチマークの多くについて説明しています。


申し訳ありませんが、これについてはあまり知りませんが、これはjavaが現在ネイティブであることを意味しますか?または、必要に応じてJavaプログラムをネイティブコードにコンパイルできる新しいコンパイラフラグがあり、バイトコードにコンパイルするオプションもまだあるということですか?
パベル

@ paulpaul1076リンクしたビデオを見ることをお勧めします。私がコメントに合理的に収めることができる以上に、かなり多くのことがあります。

4

gcj 最小限の実行可能な例

gcj(現在は廃止されている)のようなオープンソース実装を観察することもできます。たとえば、Javaファイル:

public class Main {
    public static void main(String args[]) {
        System.out.println("hello world");
    }
}

次に、コンパイルして実行します:

gcj -c Main.java
gcj --main=Main -o Main Main.o
./Main

今、あなたはそれを自由に逆コンパイルして、それがどのように機能するかを見ることができます。

file Main.o それはエルフファイルだと言います。

readelf -d Main | grep NEEDED 動的ライブラリに依存すると言います:

0x0000000000000001 (NEEDED)             Shared library: [libgcj.so.14]
0x0000000000000001 (NEEDED)             Shared library: [libc.so.6]

そのため、libgcj.soはJava機能が実装されている場所でなければなりません。

その後、次のコマンドで逆コンパイルできます。

objdump -Cdr Main.o

実装方法を正確に確認してください。

C ++、多くの名前のマングリング、間接的な多態的な関数呼び出しによく似ています。

ガベージコレクションがどのように起動するのか疑問に思います。https//stackoverflow.com/questions/7100776/garbage-collection-implementation-in-compiled-languagesおよびGoのようなGCでコンパイルされた他の言語を調べる価値があります。

Ubuntu 14.04、GCC 4.8.4でテスト済み。

また、Android 5以降のバックボーンであるhttps://en.wikipedia.org/wiki/Android_Runtimeをご覧ください。これは、完全なAOTを実行してAndroidアプリを最適化します。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.