Sådan opvarmes JVM

1. Oversigt

JVM er en af ​​de ældste endnu kraftfulde virtuelle maskiner, der nogensinde er bygget.

I denne artikel ser vi hurtigt på, hvad det betyder at varme en JVM op, og hvordan man gør det.

2. Grundlæggende om JVM-arkitektur

Hver gang en ny JVM-proces starter, indlæses alle nødvendige klasser i hukommelsen af ​​en forekomst af ClassLoader. Denne proces finder sted i tre trin:

  1. Bootstrap Class Loading: Det "Bootstrap Class Loader”Indlæser Java-kode og vigtige Java-klasser som f.eks java.lang.Objekt i hukommelsen. Disse indlæste klasser bor i JRE \ lib \ rt.jar.
  2. Extension Class Loading: ExtClassLoader er ansvarlig for at indlæse alle JAR-filer placeret på java.ext.dirs sti. I ikke-Maven eller ikke-Gradle-baserede applikationer, hvor en udvikler tilføjer JAR'er manuelt, indlæses alle disse klasser i denne fase.
  3. Applikationsklasse indlæses: AppClassLoader indlæser alle klasser i applikationsklassestien.

Denne initialiseringsproces er baseret på en skema for doven belastning.

3. Hvad varmer JVM op

Når klasseindlæsningen er afsluttet, skubbes alle vigtige klasser (brugt på tidspunktet for processtart) ind i JVM-cachen (oprindelig kode) - hvilket gør dem tilgængelige hurtigere under kørsel. Andre klasser indlæses pr. Anmodning.

Den første anmodning til en Java-webapplikation er ofte væsentligt langsommere end den gennemsnitlige svartid i løbet af procesens levetid. Denne opvarmningsperiode kan normalt tilskrives lazy class loading og just-in-time kompilering.

Når vi holder dette i tankerne, skal vi forud for applikationer med lav latenstid cache alle klasser på forhånd - så de er tilgængelige med det samme, når de åbnes ved kørsel.

Denne proces med at indstille JVM er kendt som opvarmning.

4. Niveauet kompilering

Takket være lydarkitekturen i JVM indlæses ofte anvendte metoder i den oprindelige cache under applikationens livscyklus.

Vi kan gøre brug af denne egenskab til at tvinge indlæsning af kritiske metoder ind i cachen, når en applikation starter. I den grad er vi nødt til at indstille et VM-argument med navnet Niveauet kompilering:

-XX: CompileThreshold -XX: TieredCompilation

Normalt bruger VM den tolk til at indsamle profiloplysninger om metoder, der føres ind i compileren. I det differentierede skema bruges klientcompileren ud over tolkene til at generere kompilerede versioner af metoder, der indsamler profiloplysninger om sig selv.

Da kompileret kode er væsentligt hurtigere end fortolket kode, udføres programmet med bedre ydeevne under profileringsfasen.

Applikationer, der kører på JBoss og JDK version 7 med dette VM-argument aktiveret, har tendens til at gå ned efter nogen tid på grund af en dokumenteret fejl. Problemet er rettet i JDK version 8.

Et andet punkt at bemærke her er, at for at tvinge belastning, skal vi sørge for, at alle (eller de fleste) klasser, der skal udføres, skal have adgang. Det svarer til bestemmelse af kodedækning under enhedstest. Jo mere kode der er dækket, jo bedre bliver ydeevnen.

Det næste afsnit viser, hvordan dette kan implementeres.

5. Manuel implementering

Vi implementerer muligvis en alternativ teknik til opvarmning af JVM. I dette tilfælde kan en simpel manuel opvarmning omfatte gentagelse af oprettelsen af ​​forskellige klasser tusinder af gange, så snart applikationen starter.

For det første skal vi oprette en dummy-klasse med en normal metode:

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

Dernæst skal vi oprette en klasse, der har en statisk metode, der udføres mindst 100.000 gange, så snart applikationen starter, og med hver udførelse opretter den en ny forekomst af den førnævnte dummy-klasse, vi oprettede tidligere:

offentlig klasse ManualClassLoader {beskyttet statisk tomrumsbelastning () {for (int i = 0; i <100000; i ++) {Dummy dummy = new Dummy (); dummy.m (); }}}

Nu for at måle præstationsgevinsten, er vi nødt til at oprette en hovedklasse. Denne klasse indeholder en statisk blok, der indeholder et direkte opkald til ManualClassLoader belastning () metode.

Inde i hovedfunktionen ringer vi til ManualClassLoader belastning () metode igen og fange systemtiden i nanosekunder lige før og efter vores funktionsopkald. Endelig trækker vi disse tidspunkter for at få den aktuelle udførelsestid.

Vi skal køre applikationen to gange; en gang med belastning() metodeopkald inde i den statiske blok og en gang uden denne metodeopkald:

offentlig klasse MainApplication {statisk {lang start = System.nanoTime (); ManualClassLoader.load (); lang ende = System.nanoTime (); System.out.println ("Opvarmningstid:" + (slutstart)); } offentlig statisk ugyldig hoved (String [] args) {lang start = System.nanoTime (); ManualClassLoader.load (); lang ende = System.nanoTime (); System.out.println ("Samlet tid taget:" + (slutstart)); }}

Nedenfor er resultaterne gengivet i nanosekunder:

Med opvarmningIngen opvarmningForskel(%)
1220056 8903640 730
1083797 13609530 1256
1026025 9283837 905
1024047 7234871 706
868782 9146180 1053

Som forventet viser tilgangen med opvarmning meget bedre ydeevne end den normale.

Selvfølgelig er dette en meget forenklet benchmark og giver kun noget overfladenniveau indsigt i virkningen af ​​denne teknik. Det er også vigtigt at forstå, at vi med en applikation i den virkelige verden skal varme op med de typiske kodestier i systemet.

6. Værktøjer

Vi kan også bruge flere værktøjer til at varme JVM op. Et af de mest kendte værktøjer er Java Microbenchmark Harness, JMH. Det bruges generelt til mikro-benchmarking. Når den er indlæst, den rammer gentagne gange et kodestykke og overvåger opvarmnings-iterationscyklussen.

For at bruge det skal vi tilføje en anden afhængighed af pom.xml:

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

Vi kan kontrollere den nyeste version af JMH i Central Maven Repository.

Alternativt kan vi bruge JMHs maven-plugin til at generere et eksempelprojekt:

mvn arketype: generer \ -DinteractiveMode = falsk \ -DarchetypeGroupId = org.openjdk.jmh \ -DarchetypeArtifactId = jmh-java-benchmark-arketype \ -DgroupId = com.baeldung \ -DartifactId = test \ -Dversion = 1.0

Lad os derefter oprette en vigtigste metode:

offentlig statisk ugyldig hoved (String [] args) kaster RunnerException, IOException {Main.main (args); }

Nu skal vi oprette en metode og kommentere den med JMH'er @Benchmark kommentar:

@Benchmark public void init () {// kodestykke}

Inde i dette i det metode, skal vi skrive kode, der skal udføres gentagne gange for at varme op.

7. Performance Benchmark

I de sidste 20 år var de fleste bidrag til Java relateret til GC (Garbage Collector) og JIT (Just In Time Compiler). Næsten alle de præstationsbenchmarks, der findes online, udføres på en JVM, der allerede kører i nogen tid. Imidlertid,

Imidlertid, Beihang Universitet har offentliggjort en benchmarkrapport, der tager højde for JVM-opvarmningstid. De brugte Hadoop og Spark-baserede systemer til at behandle massive data:

Her betegner HotTub det miljø, hvor JVM blev opvarmet.

Som du kan se, kan hastigheden være betydelig, især for relativt små læseoperationer - det er derfor, disse data er interessante at overveje.

8. Konklusion

I denne hurtige artikel viste vi, hvordan JVM indlæser klasser, når en applikation starter, og hvordan vi kan varme JVM op for at få et præstationsforøg.

Denne bog gennemgår flere oplysninger og retningslinjer om emnet, hvis du vil fortsætte.

Og som altid er den fulde kildekode tilgængelig på GitHub.


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