Bagaimana cara kerja kompiler Java AOT?

18

Ada beberapa alat di luar sana ( Excelsior JET , dll.) Yang mengklaim untuk mengubah aplikasi Java menjadi executable asli ( *.exe). Namun itu adalah saya pemahaman bahwa alat ini benar-benar hanya menciptakan pembungkus asli yang memanggil / menjalankan javadari shell atau command-line.

Jika pemahaman itu salah, saya tidak mengerti bagaimana itu bisa terjadi. Jika menjalankan JVM ( javaproses) pada dasarnya adalah juru bahasa kinerja tinggi, memuat bytecode dari Java classfile dengan cepat, maka saya tidak melihat bagaimana aplikasi Java (kumpulan file bytecode yang berfungsi sebagai input ke JVM) bisa menjadi benar-benar diubah menjadi executable.

Ini karena proses JVM sudah merupakan executable asli yang mengambil set file bytecode sebagai input. Untuk menggabungkan file-file bytecode dan proses JVM menjadi satu, executable asli unified tampaknya tidak mungkin tanpa sepenuhnya menulis ulang JVM dan menghapus pagar dari spesifikasi JVM.

Jadi saya bertanya: bagaimana alat-alat ini benar - benar "mengubah" file kelas Java menjadi executable asli, atau apakah mereka?

smeeb
sumber

Jawaban:

26

Semua program memiliki lingkungan runtime. Kita cenderung melupakan ini, tetapi itu ada. Lib standar untuk C yang membungkus panggilan sistem ke sistem operasi. Objective-C memiliki runtime yang membungkus semua pesan yang lewat.

Dengan Java, runtime adalah JVM. Sebagian besar implementasi Java yang dikenal orang mirip dengan HotSpot JVM yang merupakan penerjemah kode byte dan kompiler JIT.

Ini tidak harus menjadi satu-satunya implementasi. Sama sekali tidak ada yang mengatakan Anda tidak dapat membangun runtime lib-esque standar untuk Java dan mengkompilasi kode ke kode mesin asli dan menjalankannya dalam runtime yang menangani panggilan untuk objek baru ke dalam mallocs dan akses file ke panggilan sistem pada mesin. Dan itulah yang dilakukan oleh kompiler Ahead Of Time (AOT daripada JIT). Sebut itu runtime apa yang Anda akan ... Anda bisa menyebutnya implementasi JVM (dan memang mengikuti spesifikasi JVM) atau lingkungan runtime atau lib standar untuk Java. Itu ada dan pada dasarnya melakukan hal yang sama.

Ini bisa dilakukan dengan mengimplementasikan ulang javacuntuk menargetkan mesin asli (seperti yang dilakukan GCJ ). Atau itu bisa dilakukan dengan menerjemahkan kode byte yang dihasilkan oleh javacke dalam kode mesin (atau byte) untuk komputer lain - itulah yang dilakukan Android. Berdasarkan Wikipedia itulah yang dilakukan Excelsior JET ("Kompiler mengubah kode byte Java portabel menjadi executable yang dioptimalkan untuk perangkat keras dan sistem operasi (OS)" yang diinginkan), dan hal yang sama berlaku untuk RoboVM .

Ada komplikasi tambahan dengan Java yang berarti ini sangat sulit dilakukan sebagai pendekatan eksklusif. Pemuatan dinamis kelas ( class.forName()) atau objek yang diproksikan memerlukan dinamika yang tidak mudah disediakan oleh penyusun AOT dan karenanya JVM masing-masing juga harus menyertakan kompiler JIT (Excelsior JET) atau juru bahasa (GCJ) untuk menangani kelas yang tidak dapat dikompilasi menjadi asli.

Ingat, JVM adalah spesifikasi , dengan banyak implementasi . Pustaka standar C juga merupakan spesifikasi dengan banyak implementasi berbeda.

Dengan Java8, sedikit pekerjaan telah dilakukan pada kompilasi AOT. Paling-paling, seseorang hanya dapat meringkas AOT secara umum dalam batas-batas kotak teks. Namun, dalam KTT Bahasa JVM untuk 2015 (Agustus 2015), ada presentasi: Java Goes AOT (video youtube). Video ini berdurasi 40 menit dan masuk ke banyak aspek teknis yang lebih dalam dan tolok ukur kinerja.


sumber
Maaf, saya tidak tahu banyak tentang ini, tetapi apakah ini berarti bahwa java adalah asli sekarang? Atau apakah itu berarti ada flag kompiler baru yang memungkinkan kita untuk mengkompilasi program java ke kode asli jika kita mau, dan kita masih memiliki opsi untuk mengkompilasi ke kode byte, juga?
Pavel
@ paulpaul1076 Saya sarankan menonton video yang saya tautkan. Ada sedikit lebih banyak di dalamnya daripada yang bisa saya masukkan ke dalam komentar.
4

gcj contoh runnable minimal

Anda juga dapat mengamati implementasi open source seperti gcj(sekarang usang). Misalnya file Java:

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

Kemudian kompilasi dan jalankan dengan:

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

Sekarang Anda bebas untuk mendekompilasi dan melihat cara kerjanya.

file Main.o mengatakan itu adalah file peri.

readelf -d Main | grep NEEDED mengatakan itu tergantung pada perpustakaan dinamis:

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

Jadi libgcj.so harus di mana fungsi Java diimplementasikan.

Anda kemudian dapat mendekompilasi dengan:

objdump -Cdr Main.o

dan lihat bagaimana penerapannya.

Sepertinya C ++, banyak nama mangling dan panggilan fungsi polimorfik tidak langsung.

Saya bertanya-tanya bagaimana pengumpulan sampah dimulai. Akan bermanfaat untuk melihat ke: /programming/7100776/garbage-collection-implementation-in-compiled-languages dan bahasa kompilasi lainnya dengan GC seperti Go.

Diuji pada Ubuntu 14.04, GCC 4.8.4.

Lihat juga https://en.wikipedia.org/wiki/Android_Runtime , tulang punggung Android 5 dan seterusnya, yang melakukan AOT penuh untuk mengoptimalkan aplikasi Android.

Ciro Santilli 新疆 改造 中心 法轮功 六四 事件
sumber