Metode Inlining di JVM

1. Perkenalan

Dalam tutorial ini, kita akan melihat metode inlining apa yang ada di Java Virtual Machine dan cara kerjanya.

Kami juga akan melihat bagaimana mendapatkan dan membaca informasi yang terkait dengan inlining dari JVM dan apa yang dapat kami lakukan dengan informasi ini untuk mengoptimalkan kode kami.

2. Apa Metode Inlining Itu?

Pada dasarnya, penyebarisan adalah cara untuk mengoptimalkan kode sumber yang dikompilasi pada waktu proses dengan mengganti pemanggilan metode yang paling sering dieksekusi dengan tubuhnya.

Meskipun ada kompilasi yang terlibat, ini tidak dilakukan oleh kompiler javac tradisional , tetapi oleh JVM itu sendiri. Lebih tepatnya, ini adalah tanggung jawab compiler Just-In-Time (JIT) , yang merupakan bagian dari JVM; javac hanya menghasilkan bytecode dan memungkinkan JIT melakukan keajaiban dan mengoptimalkan kode sumber.

Salah satu konsekuensi terpenting dari pendekatan ini adalah jika kita mengkompilasi kode menggunakan Java lama, sama. file kelas akan lebih cepat pada JVM yang lebih baru. Dengan cara ini kita tidak perlu mengkompilasi ulang kode sumbernya, tetapi hanya memperbarui Java.

3. Bagaimana JIT Melakukannya?

Pada dasarnya, compiler JIT mencoba untuk menyebariskan metode yang sering kita panggil sehingga kita dapat menghindari overhead pemanggilan metode . Ada dua hal yang menjadi pertimbangan saat memutuskan apakah akan menyebariskan metode atau tidak.

Pertama, ini menggunakan penghitung untuk melacak berapa kali kita memanggil metode. Jika metode ini dipanggil lebih dari beberapa kali, metode tersebut menjadi "panas". Ambang batas ini disetel ke 10.000 secara default, tetapi kami dapat mengkonfigurasinya melalui bendera JVM selama startup Java. Kami pasti tidak ingin menyebariskan semuanya karena akan memakan waktu dan akan menghasilkan bytecode yang besar.

Kita harus ingat bahwa inlining hanya akan terjadi ketika kita mencapai keadaan stabil. Ini berarti kita perlu mengulang eksekusi beberapa kali untuk memberikan informasi profil yang cukup untuk compiler JIT.

Selain itu, menjadi "panas" tidak menjamin bahwa metode tersebut akan disisipkan. Jika terlalu besar, JIT tidak akan membuatnya menjadi sejajar. Ukuran yang dapat diterima dibatasi oleh tanda -XX: FreqInlineSize = , yang menentukan jumlah maksimum instruksi bytecode ke sebaris untuk suatu metode.

Namun demikian, sangat disarankan untuk tidak mengubah nilai default dari flag ini kecuali kita benar-benar yakin mengetahui dampak yang ditimbulkannya. Nilai default bergantung pada platform - untuk Linux 64-bit, 325.

JIT menghubungkan metode statis , privat , atau final secara umum . Dan meskipun metode publik juga merupakan kandidat untuk inline, tidak semua metode publik harus menggunakan inline. JVM perlu menentukan bahwa hanya ada satu implementasi dari metode semacam itu . Setiap subclass tambahan akan mencegah inline dan kinerja pasti akan menurun.

4. Menemukan Metode Panas

Kami pasti tidak ingin menebak apa yang dilakukan JIT. Oleh karena itu, diperlukan suatu cara untuk melihat metode mana yang inline atau tidak inline. Kami dapat dengan mudah mencapai ini dan mencatat semua informasi ini ke output standar dengan menyetel beberapa flag JVM tambahan selama startup:

-XX:+PrintCompilation -XX:+UnlockDiagnosticVMOptions -XX:+PrintInlining

Bendera pertama akan masuk saat kompilasi JIT terjadi. Bendera kedua mengaktifkan tanda tambahan termasuk -XX: + PrintInlining , yang akan mencetak metode apa yang disisipkan dan di mana.

Ini akan menunjukkan kepada kita metode sebaris dalam bentuk pohon. Daun diberi anotasi dan ditandai dengan salah satu opsi berikut:

  • inline (hot) - metode ini ditandai sebagai hot dan inline
  • terlalu besar - metodenya tidak panas, tetapi juga bytecode yang dihasilkannya terlalu besar, jadi tidak sebaris
  • metode panas terlalu besar - ini adalah metode panas, tetapi tidak sebaris karena bytecode terlalu besar

Kita harus memperhatikan nilai ketiga dan mencoba mengoptimalkan metode dengan label “metode panas terlalu besar”.

Umumnya, jika kita menemukan metode panas dengan pernyataan bersyarat yang sangat kompleks, kita harus mencoba untuk memisahkan konten pernyataan if dan meningkatkan perincian sehingga JIT dapat mengoptimalkan kode. Hal yang sama berlaku untuk beralih dan untuk- pernyataan lingkaran.

Kita dapat menyimpulkan bahwa inlining metode manual adalah sesuatu yang tidak perlu kita lakukan untuk mengoptimalkan kode kita. JVM melakukannya dengan lebih efisien, dan kami mungkin akan membuat kode menjadi panjang dan sulit diikuti.

4.1. Contoh

Sekarang mari kita lihat bagaimana kita dapat memeriksa ini dalam praktiknya. Pertama-tama kita akan membuat kelas sederhana yang menghitung jumlah dari N bilangan bulat positif berurutan pertama :

public class ConsecutiveNumbersSum { private long totalSum; private int totalNumbers; public ConsecutiveNumbersSum(int totalNumbers) { this.totalNumbers = totalNumbers; } public long getTotalSum() { totalSum = 0; for (int i = 0; i < totalNumbers; i++) { totalSum += i; } return totalSum; } }

Selanjutnya, metode sederhana akan menggunakan kelas untuk melakukan perhitungan:

private static long calculateSum(int n) { return new ConsecutiveNumbersSum(n).getTotalSum(); }

Terakhir, kita akan memanggil metode ini beberapa kali dan melihat apa yang terjadi:

for (int i = 1; i < NUMBERS_OF_ITERATIONS; i++) { calculateSum(i); }

Dalam proses pertama, kami akan menjalankannya 1.000 kali (kurang dari nilai ambang 10.000 yang disebutkan di atas). Jika kita mencari keluaran untuk metode hitungSum () , kita tidak akan menemukannya. Ini diharapkan karena kami tidak cukup sering menyebutnya.

Jika sekarang kita mengubah jumlah iterasi menjadi 15.000 dan mencari keluarannya lagi, kita akan melihat:

664 262 % com.baeldung.inlining.InliningExample::main @ 2 (21 bytes) @ 10 com.baeldung.inlining.InliningExample::calculateSum (12 bytes) inline (hot)

Kita dapat melihat bahwa kali ini metode tersebut memenuhi syarat untuk inline dan JVM inline.

Patut dicatat lagi bahwa jika metode ini terlalu besar, JIT tidak akan menyebariskannya, terlepas dari jumlah iterasi. Kita bisa memeriksanya dengan menambahkan flag lain saat menjalankan aplikasi:

-XX:FreqInlineSize=10

Seperti yang bisa kita lihat pada output sebelumnya, ukuran metode kita adalah 12 byte. Tanda -XX: FreqInlineSize akan membatasi ukuran metode yang memenuhi syarat untuk sebaris menjadi 10 byte. Akibatnya, penyebarisan tidak boleh dilakukan kali ini. Dan memang, kami dapat mengonfirmasi ini dengan melihat lagi pada hasilnya:

330 266 % com.baeldung.inlining.InliningExample::main @ 2 (21 bytes) @ 10 com.baeldung.inlining.InliningExample::calculateSum (12 bytes) hot method too big

Meskipun kami telah mengubah nilai flag di sini untuk tujuan ilustrasi, kami harus menekankan rekomendasi untuk tidak mengubah nilai default dari flag -XX: FreqInlineSize kecuali benar-benar diperlukan.

5. Kesimpulan

Pada artikel ini, kita melihat metode apa yang sebaris di JVM dan bagaimana JIT melakukannya. Kami menjelaskan bagaimana kami dapat memeriksa apakah metode kami memenuhi syarat untuk sebaris atau tidak dan menyarankan cara menggunakan informasi ini dengan mencoba mengurangi ukuran metode panjang yang sering disebut yang terlalu besar untuk disisipkan.

Akhirnya, kami mengilustrasikan bagaimana kami dapat mengidentifikasi metode panas dalam praktiknya.

Semua cuplikan kode yang disebutkan dalam artikel dapat ditemukan di repositori GitHub kami.