Kako zagrijati JVM

1. Pregled

JVM je jedan od najstarijih, a istovremeno moćnih virtualnih strojeva ikad izgrađenih.

U ovom ćemo članku na brzinu pogledati što znači zagrijavanje JVM-a i kako to učiniti.

2. Osnove arhitekture JVM

Kad god započne novi JVM proces, instanca ClassLoader učitava sve potrebne klase u memoriju. Taj se postupak odvija u tri koraka:

  1. Učitavanje Bootstrap klase: "Učitavač klase Bootstrap"Učitava Java kôd i osnovne Java klase kao što su java.lang.Object u sjećanje. Ove učitane klase borave u JRE \ lib \ rt.jar.
  2. Učitavanje razreda produženja: ExtClassLoader odgovoran je za učitavanje svih JAR datoteka koje se nalaze na java.ext.dirs staza. U aplikacijama koje se ne temelje na Mavenu ili ne na Gradleu, gdje programer ručno dodaje JAR-ove, sve se te klase učitavaju tijekom ove faze.
  3. Učitavanje klase aplikacije: AppClassLoader učitava sve klase smještene na putu klase aplikacije.

Ovaj postupak inicijalizacije temelji se na lijenoj shemi učitavanja.

3. Što je zagrijavanje JVM-a

Nakon što se učitavanje klase dovrši, sve važne klase (koje se koriste u vrijeme pokretanja procesa) guraju se u JVM predmemoriju (izvorni kôd) - što im omogućava brži pristup tijekom izvođenja. Ostale se klase učitavaju po zahtjevu.

Prvi zahtjev upućen Java web aplikaciji često je znatno sporiji od prosječnog vremena odgovora tijekom životnog vijeka procesa. Ovo se razdoblje zagrijavanja obično može pripisati lijenom učitavanju klase i pravovremenoj kompilaciji.

Imajući ovo na umu, za aplikacije s malim kašnjenjem moramo prethodno predmemorirati sve klase - tako da su odmah dostupne kad im se pristupi u vrijeme izvođenja.

Ovaj postupak podešavanja JVM-a poznat je kao zagrijavanje.

4. Slojevita kompilacija

Zahvaljujući zvučnoj arhitekturi JVM-a, često korištene metode učitavaju se u izvornu predmemoriju tijekom životnog ciklusa aplikacije.

Ovo svojstvo možemo koristiti za prisilno učitavanje kritičnih metoda u predmemoriju kada se aplikacija pokrene. Utoliko moramo postaviti VM argument imenovan Slojevita kompilacija:

-XX: CompileThreshold -XX: TieredCompilation

VM obično koristi tumač za prikupljanje podataka o profiliranju metoda koje se unose u kompajler. U stupnjastoj shemi, uz tumača, klijentski kompajler koristi se za generiranje kompiliranih verzija metoda koje prikupljaju podatke o profiliranju o sebi.

Budući da je prevedeni kôd znatno brži od protumačenog, program se izvršava boljim performansama tijekom faze profiliranja.

Aplikacije pokrenute na JBossu i JDK verziji 7 s omogućenim ovim VM argumentom imaju tendenciju pada nakon nekog vremena zbog dokumentirane pogreške. Problem je riješen u JDK verziji 8.

Još jedna napomena koju treba napomenuti ovdje je da, da bismo forsirali opterećenje, moramo biti sigurni da treba pristupiti svim (ili većini) klasa koje će se izvršiti. Slično je određivanju pokrivenosti koda tijekom jediničnog testiranja. Što je više koda pokriveno, to će performanse biti bolje.

Sljedeći odjeljak pokazuje kako se to može primijeniti.

5. Ručna provedba

Možemo primijeniti zamjensku tehniku ​​za zagrijavanje JVM-a. U ovom slučaju, jednostavno ručno zagrijavanje moglo bi uključivati ​​ponavljanje stvaranja različitih klasa tisućama puta čim aplikacija započne.

Prvo, moramo stvoriti lažnu klasu normalnom metodom:

javna klasa lutka {javna praznina m () {}}

Dalje, moramo stvoriti klasu koja ima statičku metodu koja će se izvršiti najmanje 100000 puta čim se aplikacija pokrene i sa svakim izvršavanjem stvara novu instancu gore spomenute lažne klase koju smo ranije kreirali:

javna klasa ManualClassLoader {zaštićeno statičko void opterećenje () {za (int i = 0; i <100000; i ++) {Lutka lutka = nova lutka (); lutka.m (); }}}

Sada, kako bi izmjerite dobitak u izvedbi, moramo stvoriti glavnu klasu. Ova klasa sadrži jedan statički blok koji sadrži izravan poziv na Učitavanje ManualClassLoader () metoda.

Unutar glavne funkcije pozivamo Učitavanje ManualClassLoader () metodu još jednom i zabilježite sistemsko vrijeme u nanosekundama neposredno prije i nakon poziva naše funkcije. Konačno, oduzimamo ta vremena da bismo dobili stvarno vrijeme izvršenja.

Morali bismo pokrenuti aplikaciju dva puta; jednom s opterećenje() poziv metode unutar statičkog bloka i jednom bez poziva ove metode:

javna klasa MainApplication {static {long start = System.nanoTime (); ManualClassLoader.load (); dugi kraj = System.nanoTime (); System.out.println ("Vrijeme zagrijavanja:" + (kraj - početak)); } javna statička void glavna (String [] args) {long start = System.nanoTime (); ManualClassLoader.load (); dugi kraj = System.nanoTime (); System.out.println ("Ukupno vrijeme potrebno:" + (kraj - početak)); }}

Ispod se prikazuju rezultati u nanosekundama:

Uz Warm UpNema zagrijavanjaRazlika(%)
1220056 8903640 730
1083797 13609530 1256
1026025 9283837 905
1024047 7234871 706
868782 9146180 1053

Očekivano, pristup zagrijavanju pokazuje puno bolje performanse od uobičajenog.

Naravno, ovo je vrlo pojednostavljeno mjerilo i pruža samo neki uvid na površini o utjecaju ove tehnike. Također, važno je shvatiti da se sa stvarnom aplikacijom moramo zagrijati s tipičnim putovima koda u sustavu.

6. Alati

Također možemo koristiti nekoliko alata za zagrijavanje JVM-a. Jedan od najpoznatijih alata je Java Microbenchmark Harness, JMH. Obično se koristi za mikro-benchmarking. Jednom kad se učita, opetovano pogađa isječak koda i nadgleda ciklus ponavljanja zagrijavanja.

Da bismo ga koristili, moramo dodati još jednu ovisnost na pom.xml:

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

Najnoviju verziju JMH možemo provjeriti u Central Maven Repository.

Alternativno, možemo koristiti dodatak maven JMH za generiranje uzorka projekta:

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

Dalje, izradimo a glavni metoda:

javna statička void glavna (String [] args) baca RunnerException, IOException {Main.main (args); }

Sada moramo stvoriti metodu i označiti je s JMH-ima @Benchmark napomena:

@Benchmark javna void init () {// isječak koda}

Unutar ovoga u tome metodu, trebamo napisati kod koji treba izvoditi više puta kako bi se zagrijao.

7. Mjerilo performansi

U posljednjih 20 godina većina doprinosa Javi odnosila se na GC (skupljač smeća) i JIT (Just In Time Compiler). Gotovo sva mjerila performansi pronađena na mreži rade se na JVM-u koji već neko vrijeme radi. Međutim,

Međutim, Sveučilište Beihang objavio je referentno izvješće uzimajući u obzir vrijeme zagrijavanja JVM-a. Koristili su sustave temeljene na Hadoop i Spark za obradu masovnih podataka:

Ovdje HotTub označava okruženje u kojem je JVM zagrijan.

Kao što vidite, ubrzanje može biti značajno, posebno za relativno male operacije čitanja - zbog čega je zanimljivo razmotriti ove podatke.

8. Zaključak

U ovom kratkom članku pokazali smo kako JVM učitava klase kada se aplikacija pokreće i kako možemo zagrijati JVM kako bismo postigli poboljšanje performansi.

Ova knjiga donosi više informacija i smjernica na tu temu ako želite nastaviti.

I kao i uvijek, puni izvorni kod dostupan je na GitHubu.


$config[zx-auto] not found$config[zx-overlay] not found