Bagaimana Melakukan Pemanasan JVM

1. Ikhtisar

JVM adalah salah satu mesin virtual tertua namun kuat yang pernah dibuat.

Pada artikel ini, kita akan melihat sekilas apa artinya menghangatkan JVM dan bagaimana melakukannya.

2. Dasar-dasar Arsitektur JVM

Setiap kali proses JVM baru dimulai, semua kelas yang diperlukan dimuat ke dalam memori oleh instance ClassLoader. Proses ini berlangsung dalam tiga langkah:

  1. Pemuatan Kelas Bootstrap: " Pemuat Kelas Bootstrap " memuat kode Java dan kelas-kelas penting Java seperti java.lang.Object ke dalam memori. Kelas yang dimuat ini berada di JRE \ lib \ rt.jar .
  2. Pemuatan Kelas Ekstensi : ExtClassLoader bertanggung jawab untuk memuat semua file JAR yang terletak di jalur java.ext.dirs . Dalam aplikasi berbasis non-Maven atau non-Gradle, di mana developer menambahkan JAR secara manual, semua kelas tersebut dimuat selama fase ini.
  3. Pemuatan Kelas Aplikasi : AppClassLoader memuat semua kelas yang terletak di jalur kelas aplikasi.

Proses inisialisasi ini didasarkan pada skema lazy loading.

3. Apa yang Memanaskan JVM

Setelah pemuatan kelas selesai, semua kelas penting (digunakan saat proses dimulai) didorong ke cache JVM (kode asli) - yang membuatnya dapat diakses lebih cepat selama waktu proses. Kelas lain dimuat berdasarkan permintaan.

Permintaan pertama yang dibuat untuk aplikasi web Java seringkali jauh lebih lambat daripada waktu respons rata-rata selama masa proses. Periode pemanasan ini biasanya dapat dikaitkan dengan lazy class loading dan kompilasi just-in-time.

Dengan mengingat hal ini, untuk aplikasi latensi rendah, kita perlu menyimpan cache semua kelas sebelumnya - sehingga kelas tersebut tersedia secara instan saat diakses saat runtime.

Proses penyetelan JVM ini dikenal sebagai pemanasan.

4. Kompilasi Bertingkat

Berkat arsitektur suara JVM, metode yang sering digunakan dimuat ke cache asli selama siklus hidup aplikasi.

Kita dapat menggunakan properti ini untuk memuat paksa metode kritis ke dalam cache saat aplikasi dimulai. Untuk itu, kita perlu menyetel argumen VM bernama Kompilasi Bertingkat :

-XX:CompileThreshold -XX:TieredCompilation

Biasanya, VM menggunakan interpreter untuk mengumpulkan informasi profil tentang metode yang dimasukkan ke dalam compiler. Dalam skema berjenjang, selain interpreter, compiler klien digunakan untuk menghasilkan versi terkompilasi dari metode yang mengumpulkan informasi profil tentang diri mereka sendiri.

Karena kode yang dikompilasi secara substansial lebih cepat daripada kode yang diinterpretasikan, program dijalankan dengan kinerja yang lebih baik selama fase pembuatan profil.

Aplikasi yang berjalan pada JBoss dan JDK versi 7 dengan argumen VM ini diaktifkan cenderung macet setelah beberapa waktu karena bug yang terdokumentasi. Masalah telah diperbaiki di JDK versi 8.

Hal lain yang perlu diperhatikan di sini adalah bahwa untuk memaksa memuat, kita harus memastikan bahwa semua (atau sebagian besar) kelas yang akan dieksekusi perlu diakses. Ini mirip dengan menentukan cakupan kode selama pengujian unit. Semakin banyak kode yang dibahas, semakin baik performanya.

Bagian selanjutnya menunjukkan bagaimana hal ini dapat diterapkan.

5. Penerapan Manual

Kami mungkin menerapkan teknik alternatif untuk memanaskan JVM. Dalam kasus ini, pemanasan manual sederhana dapat mencakup pengulangan pembuatan kelas yang berbeda ribuan kali segera setelah aplikasi dimulai.

Pertama, kita perlu membuat kelas dummy dengan metode normal:

public class Dummy { public void m() { } }

Selanjutnya, kita perlu membuat kelas yang memiliki metode statis yang akan dieksekusi setidaknya 100000 kali segera setelah aplikasi dimulai dan dengan setiap eksekusi, itu membuat instance baru dari kelas dummy yang disebutkan sebelumnya yang kita buat sebelumnya:

public class ManualClassLoader { protected static void load() { for (int i = 0; i < 100000; i++) { Dummy dummy = new Dummy(); dummy.m(); } } }

Sekarang, untuk mengukur perolehan kinerja , kita perlu membuat kelas utama. Kelas ini berisi satu blok statis yang berisi panggilan langsung ke metode load () ManualClassLoader .

Di dalam fungsi utama, kita membuat panggilan ke metode load () ManualClassLoader sekali lagi dan merekam waktu sistem dalam nanodetik tepat sebelum dan sesudah panggilan fungsi kita. Terakhir, kami mengurangi waktu ini untuk mendapatkan waktu eksekusi sebenarnya.

Kami telah menjalankan aplikasi dua kali; sekali dengan pemanggilan metode load () di dalam blok statis dan sekali tanpa pemanggilan metode ini:

public class MainApplication { static { long start = System.nanoTime(); ManualClassLoader.load(); long end = System.nanoTime(); System.out.println("Warm Up time : " + (end - start)); } public static void main(String[] args) { long start = System.nanoTime(); ManualClassLoader.load(); long end = System.nanoTime(); System.out.println("Total time taken : " + (end - start)); } }

Di bawah hasil direproduksi dalam nanodetik:

Dengan Pemanasan Tidak Ada Pemanasan Perbedaan(%)
1220056 8903640 730
1083797 13609530 1256
1026025 9283837 905
1024047 7234871 706
868782 9146180 1053

Seperti yang diharapkan, dengan pendekatan pemanasan menunjukkan kinerja yang jauh lebih baik daripada yang normal.

Tentu saja, ini adalah tolok ukur yang sangat sederhana dan hanya memberikan beberapa wawasan tingkat permukaan tentang dampak teknik ini. Selain itu, penting untuk dipahami bahwa, dengan aplikasi dunia nyata, kita perlu melakukan pemanasan dengan jalur kode tipikal dalam sistem.

6. Alat

We can also use several tools to warm up the JVM. One of the most well-known tools is the Java Microbenchmark Harness, JMH. It's generally used for micro-benchmarking. Once it is loaded, it repeatedly hits a code snippet and monitors the warm-up iteration cycle.

To use it we need to add another dependency to the pom.xml:

 org.openjdk.jmh jmh-core 1.19   org.openjdk.jmh jmh-generator-annprocess 1.19 

We can check the latest version of JMH in Central Maven Repository.

Alternatively, we can use JMH's maven plugin to generate a sample project:

mvn archetype:generate \ -DinteractiveMode=false \ -DarchetypeGroupId=org.openjdk.jmh \ -DarchetypeArtifactId=jmh-java-benchmark-archetype \ -DgroupId=com.baeldung \ -DartifactId=test \ -Dversion=1.0

Next, let's create a main method:

public static void main(String[] args) throws RunnerException, IOException { Main.main(args); }

Now, we need to create a method and annotate it with JMH's @Benchmark annotation:

@Benchmark public void init() { //code snippet }

Inside this init method, we need to write code that needs to be executed repeatedly in order to warm up.

7. Performance Benchmark

In the last 20 years, most contributions to Java were related to the GC (Garbage Collector) and JIT (Just In Time Compiler). Almost all of the performance benchmarks found online are done on a JVM already running for some time. However,

However, Beihang University has published a benchmark report taking into account JVM warm-up time. They used Hadoop and Spark based systems to process massive data:

Here HotTub designates the environment in which the JVM was warmed up.

As you can see, the speed-up can be significant, especially for relatively small read operations – which is why this data is interesting to consider.

8. Conclusion

In this quick article, we showed how the JVM loads classes when an application starts and how we can warm up the JVM in order gain a performance boost.

Buku ini membahas lebih banyak informasi dan pedoman tentang topik tersebut jika Anda ingin melanjutkan.

Dan, seperti biasa, kode sumber lengkap tersedia di GitHub.