JavaRush /Java Blog /Random-ID /Mengompilasi dan mengeksekusi aplikasi Java secara tersem...
Павел Голов
Level 34
Москва

Mengompilasi dan mengeksekusi aplikasi Java secara tersembunyi

Dipublikasikan di grup Random-ID

Isi:

  1. Perkenalan
  2. Kompilasi ke bytecode
  3. Contoh kompilasi dan eksekusi program
  4. Mengeksekusi program pada mesin virtual
  5. Kompilasi tepat waktu (JIT).
  6. Kesimpulan
Mengompilasi dan mengeksekusi aplikasi Java secara tersembunyi - 1

1. Perkenalan

Halo semua! Hari ini saya ingin berbagi pengetahuan tentang apa yang terjadi di bawah kap JVM (Java Virtual Machine) setelah kita menjalankan aplikasi tertulis Java. Saat ini, terdapat lingkungan pengembangan trendi yang membantu Anda menghindari memikirkan internal JVM, mengkompilasi dan mengeksekusi kode Java, yang dapat menyebabkan pengembang baru melewatkan aspek-aspek penting ini. Pada saat yang sama, pertanyaan mengenai topik ini sering ditanyakan selama wawancara, itulah sebabnya saya memutuskan untuk menulis artikel.

2. Kompilasi ke bytecode

Mengompilasi dan mengeksekusi aplikasi Java secara tersembunyi - 2
Mari kita mulai dengan teorinya. Saat kami menulis aplikasi apa pun, kami membuat file dengan ekstensi .javadan menempatkan kode di dalamnya dalam bahasa pemrograman Java. File yang berisi kode yang dapat dibaca manusia disebut file kode sumber . Setelah file kode sumber siap, Anda perlu menjalankannya! Namun pada tahap ini berisi informasi yang hanya dapat dipahami oleh manusia. Java adalah bahasa pemrograman multi-platform. Ini berarti bahwa program yang ditulis dalam Java dapat dijalankan pada platform apa pun yang memiliki sistem runtime Java khusus yang terinstal. Sistem ini disebut Java Virtual Machine (JVM). Untuk menerjemahkan program dari kode sumber ke dalam kode yang dapat dimengerti oleh JVM, Anda perlu mengkompilasinya. Kode yang dipahami JVM disebut bytecode dan berisi sekumpulan instruksi yang selanjutnya akan dijalankan oleh mesin virtual. Untuk mengkompilasi kode sumber menjadi bytecode, ada compiler javacyang disertakan dalam JDK (Java Development Kit). Sebagai masukan, kompiler menerima file dengan ekstensi .java, yang berisi kode sumber program, dan sebagai keluaran, ia menghasilkan file dengan ekstensi .class, yang berisi bytecode yang diperlukan agar program dapat dieksekusi oleh mesin virtual. Setelah suatu program dikompilasi menjadi bytecode, program tersebut dapat dijalankan menggunakan mesin virtual.

3. Contoh kompilasi dan eksekusi program

Misalkan kita memiliki program sederhana, yang terdapat dalam sebuah file Calculator.java, yang mengambil 2 argumen baris perintah numerik dan mencetak hasil penambahannya:
class Calculator {
    public static void main(String[] args){
        int a = Integer.valueOf(args[0]);
        int b = Integer.valueOf(args[1]);

        System.out.println(a + b);
    }
}
Untuk mengkompilasi program ini menjadi bytecode, kita akan menggunakan compiler javacpada baris perintah:
javac Calculator.java
Setelah kompilasi, kami menerima file dengan bytecode sebagai output Calculator.class, yang dapat kami jalankan menggunakan mesin java yang diinstal di komputer kami menggunakan perintah java pada baris perintah:
java Calculator 1 2
Perhatikan bahwa setelah nama file, 2 argumen baris perintah ditentukan - angka 1 dan 2. Setelah menjalankan program, angka 3 akan ditampilkan pada baris perintah. Dalam contoh di atas, kami memiliki kelas sederhana yang hidup sendiri . Tetapi bagaimana jika kelas tersebut ada dalam suatu paket? Mari kita simulasikan situasi berikut: buat direktori src/ru/javarushdan tempatkan kelas kita di sana. Sekarang tampilannya seperti ini (kami menambahkan nama paket di awal file):
package ru.javarush;

class Calculator {
    public static void main(String[] args){
        int a = Integer.valueOf(args[0]);
        int b = Integer.valueOf(args[1]);

        System.out.println(a + b);
    }
}
Mari kita kompilasi kelas tersebut dengan perintah berikut:
javac -d bin src/ru/javarush/Calculator.java
Dalam contoh ini, kami menggunakan opsi kompiler tambahan -d binyang menempatkan file yang dikompilasi ke dalam direktori bindengan struktur yang mirip dengan direktori src, tetapi direktori tersebut binharus dibuat terlebih dahulu. Teknik ini digunakan untuk menghindari kebingungan file kode sumber dengan file bytecode. Sebelum menjalankan program yang dikompilasi, ada baiknya menjelaskan konsepnya classpath. Classpathadalah jalur relatif tempat mesin virtual akan mencari paket dan kelas yang dikompilasi. Artinya, dengan cara ini kami memberi tahu mesin virtual direktori mana dalam sistem file yang merupakan akar hierarki paket Java. Classpathdapat ditentukan saat memulai program menggunakan flag -classpath. Kami meluncurkan program menggunakan perintah:
java -classpath ./bin ru.javarush.Calculator 1 2
Dalam contoh ini, kami memerlukan nama lengkap kelas, termasuk nama paket di mana kelas tersebut berada. Pohon file terakhir terlihat seperti ini:
├── src
│     └── ru
│          └── javarush
│                  └── Calculator.java
└── bin
      └── ru
           └── javarush
                   └── Calculator.class

4. Eksekusi program oleh mesin virtual

Jadi, kami meluncurkan program tertulis. Namun apa yang terjadi jika program yang dikompilasi diluncurkan oleh mesin virtual? Pertama, mari kita cari tahu apa arti konsep kompilasi dan interpretasi kode. Kompilasi adalah penerjemahan program yang ditulis dalam bahasa sumber tingkat tinggi ke dalam program setara dalam bahasa tingkat rendah yang mirip dengan kode mesin. Interpretasi adalah analisis operator demi pernyataan (perintah demi baris, baris demi baris), pemrosesan, dan eksekusi langsung dari program atau permintaan sumber (sebagai lawan dari kompilasi, di mana program diterjemahkan tanpa mengeksekusinya). Bahasa Java memiliki compiler ( javac) dan interpreter, yaitu mesin virtual yang mengubah bytecode menjadi kode mesin baris demi baris dan segera mengeksekusinya. Jadi, ketika kita menjalankan program yang dikompilasi, mesin virtual mulai menafsirkannya, yaitu konversi bytecode baris demi baris menjadi kode mesin, serta eksekusinya. Sayangnya, interpretasi bytecode murni memerlukan proses yang cukup panjang dan membuat java lambat dibandingkan kompetitornya. Untuk menghindari hal ini, mekanisme diperkenalkan untuk mempercepat interpretasi bytecode oleh mesin virtual. Mekanisme ini disebut kompilasi Just-in-time (JITC).

5. Kompilasi tepat waktu (JIT).

Secara sederhana mekanisme kompilasi Just-In-Time adalah sebagai berikut: jika ada bagian kode dalam program yang dieksekusi berkali-kali, maka bagian tersebut dapat dikompilasi satu kali menjadi kode mesin untuk mempercepat eksekusinya di kemudian hari. Setelah mengkompilasi bagian program tersebut ke dalam kode mesin, dengan setiap panggilan berikutnya ke bagian program ini, mesin virtual akan segera mengeksekusi kode mesin yang telah dikompilasi daripada menafsirkannya, yang secara alami akan mempercepat eksekusi program. Mempercepat program dicapai dengan meningkatkan konsumsi memori (kita perlu menyimpan kode mesin yang dikompilasi di suatu tempat!) dan dengan meningkatkan waktu yang dihabiskan untuk kompilasi selama eksekusi program. Kompilasi JIT adalah mekanisme yang agak rumit, jadi mari kita bahas lebih lanjut. Ada 4 level kompilasi JIT bytecode ke dalam kode mesin. Semakin tinggi level kompilasi, semakin kompleks, tetapi pada saat yang sama, eksekusi bagian seperti itu akan lebih cepat daripada bagian dengan level yang lebih rendah. JIT - Kompiler memutuskan tingkat kompilasi yang akan ditetapkan untuk setiap fragmen program berdasarkan seberapa sering fragmen tersebut dieksekusi. Di bawah tenda, JVM menggunakan 2 kompiler JIT - C1 dan C2. Kompiler C1 disebut juga kompiler klien dan hanya mampu mengkompilasi kode hingga tingkat ke-3. Kompiler C2 bertanggung jawab atas tingkat kompilasi ke-4, paling kompleks dan tercepat.
Mengompilasi dan mengeksekusi aplikasi Java secara tersembunyi - 3
Dari uraian di atas, kita dapat menyimpulkan bahwa untuk aplikasi klien sederhana, lebih menguntungkan menggunakan kompiler C1, karena dalam hal ini penting bagi kita seberapa cepat aplikasi dimulai. Aplikasi sisi server yang berumur panjang mungkin membutuhkan waktu lebih lama untuk diluncurkan, tetapi di masa depan aplikasi tersebut harus bekerja dan menjalankan fungsinya dengan cepat - di sini kompiler C2 cocok untuk kita. Saat menjalankan program Java pada JVM versi x32, kita dapat secara manual menentukan mode mana yang ingin kita gunakan menggunakan and -clientflags -server. Jika tanda ini ditentukan, -clientJVM tidak akan melakukan optimasi bytecode yang rumit, yang akan mempercepat waktu startup aplikasi dan mengurangi jumlah memori yang dikonsumsi. Saat menentukan tanda, -serveraplikasi akan membutuhkan waktu lebih lama untuk memulai karena optimalisasi bytecode yang kompleks dan akan menggunakan lebih banyak memori untuk menyimpan kode mesin, namun program akan berjalan lebih cepat di masa mendatang. Dalam JVM versi x64, flag -clientdiabaikan dan konfigurasi server aplikasi digunakan secara default.

6. Kesimpulan

Ini menyimpulkan gambaran singkat saya tentang cara kerja kompilasi dan eksekusi aplikasi Java. Poin utama:
  1. Kompiler javac mengubah kode sumber program menjadi bytecode yang dapat dijalankan pada platform apa pun tempat mesin virtual Java diinstal;
  2. Setelah kompilasi, JVM menafsirkan bytecode yang dihasilkan;
  3. Untuk mempercepat aplikasi Java, JVM menggunakan mekanisme kompilasi Just-In-Time yang mengubah bagian program yang paling sering dieksekusi menjadi kode mesin dan menyimpannya dalam memori.
Saya harap artikel ini membantu Anda mendapatkan pemahaman lebih dalam tentang cara kerja bahasa pemrograman favorit kami. Terima kasih telah membaca, kritik diterima!
Komentar
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION