Memahami Proses Kompilasi Pada Java HotSpot Virtual Machine


Salah satu kelebihan utama Java adalah program yang dibuat dengan Java dapat dijalankan di banyak platform tanpa perubahan. Untuk memungkinkan terjadinya hal ini, program Java tidak dijalankan secara langsung oleh sistem operasi melainkan melalui apa yang disebut sebagai Java Virtual Machine (JVM). Implementasi JVM dari Oracle disebut sebagai HotSpot Virtual Machine. HotSpot adalah virtual machine yang diperoleh pada saat pengguna men-download JDK atau JRE dari situs Oracle dan juga bagian dari OpenJDK. Selain HotSpot sebagai implementasi resmi, pengguna juga dapat men-download implementasi JVM lain seperti Oracle JRockit dan IBM J9.

Sesuai namanya, HotSpot tidak serta merta menerjemahkan seluruh bytecode menjadi bahasa mesin, tetapi ia mendeteksi dan mengoptimalkan hanya bagian tertentu dari kode program. HotSpot adalah interpreter sekaligus compiler. Tidak sederhana, bukan? Bukan hanya itu, perilaku HotSpot juga bisa berubah bila ada parameter -client atau -server. Penggunaan -client akan mengaktifkan modus kompilasi C1 dimana HotSpot akan berusaha mempercepat waktu start-up dan mengurangi pemakaian memori. Sementara itu, penggunaan -server akan mengaktifkan modus kompilasi C2 dimana HotSpot akan melakukan optimalisasi kinerja agar program berjalan lebih cepat (dengan konsekuensi waktu start-up menjadi lebih lambat dan memakai lebih banyak memori). Sejak Java 7, parameter -XX:+TieredCompilation dapat digunakan untuk memperoleh hasil kombinasi keduanya (tiered compilation menjadi default di Java 8). Pada modus ini, C1 akan aktif pada saat aplikasi baru saja dijalankan, setelah itu modus kompilasi perlahan2 diambil alih oleh C2.

Untuk mendapatkan informasi mengenai proses kompilasi yang berlangsung secara dinamis saat program berjalan, saya dapat menambahkan parameter berikut ini pada saat menjalankan HotSpot:

java -XX:+UnlockDiagnosticVMOptions -XX:+LogCompilation ...

Setelah program dijalankan, HotSpot akan membuat file XML bernama hotspot.log di direktori tersebut. File hotspot.log ini berisi berbagai informasi kompilasi yang terjadi secara dinamis.

Pada sebuah program Java yang sama dengan byte code yang sama, bahasa mesin yang dihasilkan pada saat program dijalankan bisa sangat berbeda tergantung pada apakah C1 atau C2 yang aktif. Saya dapat menggunakan -XX:+PrintAssembly untuk melihat kode bahasa mesin yang dihasilkan dari proses kompilasi:

java -XX:+UnlockDiagnosticVMOptions -XX:+PrintAssembly ...

Bahasa mesin untuk masing-masing platform pengguna hanya akan dihasilkan pada saat program dijalankan. Ingat bahwa program bisa dijalankan di beberapa platform berbeda seperti Windows, Linux, Mac OS dan sebagainya. Sebelum bisa menampilkan bahasa mesin dalam bentuk bahasa assembly di platform dimana program dijalankan, saya perlu terlebih dahulu men-install plugin HSDIS. Karena memakai Windows, file plugin HSDIS yang dibutuhkan adalah hsdis-i386.dll yang harus diletakkan pada lokasi %JAVA_HOME%\bin\client dan %JAVA_HOME%\bin\server. Cara paling gampang adalah dengan men-download-nya di lokasi http://classparser.blogspot.com/2010/03/hsdis-i386dll.html.

Sekarang, bila program dijalankan, saya akan menemukan hasil yang bisa dikerjakan oleh mesin dalam bentuk bahasa assembly seperti:

...
0x01f95221: mov    %fs:0x0,%esi
0x01f95229: mov    0xfffffff4(%esi),%esi
0x01f9522c: mov    0x190(%esi),%eax
0x01f95232: movl   $0x0,0x190(%esi)
0x01f9523c: movl   $0x0,0x194(%esi)
0x01f95246: add    $0x58,%esp
0x01f95249: pop    %ebp
0x01f9524a: jmp    0x01dfd440         ;   {runtime_call}
...

Ini adalah bahasa assembly dalam syntax AT&T. Karena lebih terbiasa dengan syntax Intel, saya dapat menambahkan perintah parameter seperti:

java -XX:+UnlockDiagnosticVMOptions -XX:+PrintAssembly -XX:PrintAssemblyOptions=intel ...

Sekarang, saya akan memperoleh hasil assembly dalam syntax Intel seperti:

...
0x01f0b136: mov    ebx,ecx
0x01f0b138: mov    ecx,DWORD PTR fs:0x0
0x01f0b140: mov    ecx,DWORD PTR [ecx-12]
0x01f0b143: mov    eax,DWORD PTR [ecx+52]
0x01f0b146: lea    edi,[eax+16]
0x01f0b149: cmp    edi,DWORD PTR [ecx+60]
0x01f0b14c: ja     0x01f0b290
...

Masalah berikutnya yang muncul adalah bagaimana saya bisa memahami isi file hotspot.log yang dihasilkan oleh HotSpot? Salah satu cara mudah adalah dengan memakai JITWatch untuk menampilkan visualisasinya. JITWatch adalah proyek open source yang dapat dijumpai di https://github.com/AdoptOpenJDK/jitwatch.

Agar log dapat dianalisa oleh JITWatch, saya perlu menambahkan parameter -XX:+TraceClassLoading sehingga pada akhirnya saya menjalankan program dengan memakai perintah seperti:

java -XX:+UnlockDiagnosticVMOptions -XX:+PrintAssembly -XX:+TraceClassLoading 
-XX:+LogCompilation -XX:PrintAssemblyOptions=intel ...

Langkah pertama yang saya lakukan setelah menjalankan JITWatch adalah men-klik tombol Open Log dan memilih file hotspot.log yang telah dihasilkan. Setelah itu, saya men-klik tombol Config untuk menambahkan file source dan file class yang berkaitan dengan program tersebut.

Untuk memulai analisa file log, saya men-klik tombol Start. Setelah proses analisa selesai, saya akan memperoleh tampilan seperti pada gambar berikut ini:

Tampilan awal JITWatch

Tampilan awal JITWatch

Class yang memiliki tanda centang hijau adalah class yang method-nya sudah di-compile menjadi bahasa mesin. Bila saya memilih class tersebut, saya bisa melihat method apa saja yang sudah di-compile di sisi kanan layar. Method yang sudah di-compile akan memiliki tanda centang hijau.

Bila men-klik tombol Chart, saya akan memperoleh hasil seperti pada gambar berikut ini:

Chart di JITWatch

Chart di JITWatch

Pada gambar tersebut, terlihat bahwa terdapat 834 operasi kompilasi oleh C1 dan tidak ada operasi kompilasi oleh C2. Ada 60 operasi kompilas C2N (Compiled-to-Native atau Native Wrapper) dan 1 jenis kompilasi OSR (On Stack Replacement). Selain itu, saya juga bisa melihat kapan method yang sedang saya pilih di-compile oleh HotSpot.

Saya bisa men-klik tombol TriView untuk melihat perbandingan kode program Java, byte code (hasil kompilasi oleh developer) dan assembly (hasil kompilasi oleh HotSpot). Hasilnya akan terlihat seperti pada gambar berikut ini:

TriView di JITWatch

TriView di JITWatch

Perlu diingat bahwa proses kompilasi yang saya bicarakan adalah proses kompilasi yang berlangsung dinamis pada saat program dijalankan. HotSpot adalah kombinasi antara interpreter dan compiler. Pada saat program Java dijalankan, HotSpot akan menjalankan bytecode yang sebelumnya sudah di-compile oleh programmer (misalnya dalam bentuk file class di dalam JAR). Disini ia bekerja seperti interpreter yang mengerjakan bytecode satu per satu. Akan tetapi bila ia menemukan bagian yang dapat dioptimalkan, HotSpot akan melakukan kompilasi bytecode tersebut menjadi bahasa mesin. Dengan demikian, seseorang tidak akan pernah mengetahui seperti apa bahasa mesin yang dihasilkan oleh sebuah program Java sebelum menjalankan program tersebut.

Masih pada tampilan TriView, saya dapat men-klik tombol View Compile Chain untuk melihat informasi seperti pada gambar berikut ini:

Compile Chain di JITWatch

Compile Chain di JITWatch

Tampilan ini akan memberikan informasi kompilasi untuk method lain yang dipanggil oleh method yang sedang ditampilkan. Tampilan ini juga akan menampilkan informasi inlining. Proses inlining adalah proses dimana HotSpot melakukan optimasi pemanggilan method (yang berukuran kecil) dengan menduplikasi method yang dipanggil ke method yang memanggil. Karena method di-‘copy’ secara langsung ke pemanggil, maka tidak ada operasi pemanggilan method yang seharusnya melibatkan call stack . Hal ini akan membuat program bekerja lebih cepat.

Inlining yang pintar bisa membuat Java lebih cepat daripada kode program native yang di-compile secara statis. Akan tetapi, inlining tidak selalu bisa dipakai. Untuk melihat penyebab kegagalan inlining, saya dapat men-klik tombol TopList dan memilih Inlining Failure Reason. Saya akan memperoleh tampilan seperti pada gambar berikut ini:

Inlining failure reasons di JITWatch

Inlining failure reasons di JITWatch

Untuk membuktikan bahwa perilaku kompilasi oleh HotSpot bisa sangat berbeda, saya akan mencoba menggunakan parameter -server pada saat menjalankan program Java. Setelah memperoleh hotspot.log, saya segera membuka file tersebut di JITWatch. Bila saya men-klik tombol Chart, kali ini saya akan memperoleh tampilan seperti pada gambar berikut ini:

Tampilan chart setelah memakai -server

Tampilan chart setelah memakai -server

Bertolak belakang dengan hasil sebelumnya, kali ini hampir seluruh operasi kompilasi dilakukan oleh C2, bukan lagi oleh C1.

Walaupun waktu startup dan penggunaan memori menjadi semakin besar, tapi tampaknya ada lebih banyak optimalisasi yang dilakukan. Sebagai contoh, saya menemukan adanya pemanggilan instrinsic method seperti yang terlihat pada gambar berikut ini:

Intrinsic method yang ditampilkan JITWatch

Intrinsic method yang ditampilkan JITWatch

Instrinsic method adalah substitusi method ke bahasa mesin yang sudah dioptimalkan untuk platform yang sedang aktif.

Bila men-klik tombol TriView,View Compile Chain, saya kini menemukan lebih banyak method yang di-inline, seperti yang terlihat pada gambar berikut ini:

Inlining yang ditampilkan JITWatch

Inlining yang ditampilkan JITWatch

Perihal Solid Snake
I'm nothing...

Apa komentar Anda?

Please log in using one of these methods to post your comment:

Logo WordPress.com

You are commenting using your WordPress.com account. Logout / Ubah )

Gambar Twitter

You are commenting using your Twitter account. Logout / Ubah )

Foto Facebook

You are commenting using your Facebook account. Logout / Ubah )

Foto Google+

You are commenting using your Google+ account. Logout / Ubah )

Connecting to %s

%d blogger menyukai ini: