Java >> Java tutorial >  >> Java

Adaptiv bunkestørrelse

Mens jeg forbedrede vores testbed for at forbedre Plumbr GC-problemdetektoren, endte jeg med at skrive en lille testcase, troede jeg kan være interessant for et bredere publikum. Målet, jeg jagtede, var at teste JVM's selvtilpasningsevne i forhold til, hvordan bunken er segmenteret mellem eden, overlevende og faste rum.

Selve testen genererer objekter i batches. Batches genereres en gang i sekundet, og hver batch er 500 KB i størrelse. Disse objekter refereres i fem sekunder, derefter fjernes referencerne, og objekter fra denne særlige batch er berettiget til affaldsindsamling.

Testen blev kørt med Oracle Hotspot 7 JVM på Mac OS X ved hjælp af ParallelGC og får 30 MB heap-plads at arbejde med. Når vi kender platformen, kan vi forvente, at JVM vil starte med følgende heap-konfiguration:

  • JVM'en starter med 10 MB i Young og 20 MB i Tenured space, da JVM'en uden eksplicit konfiguration bruger 1:2-forholdet til at fordele heap mellem Young og Tenured spaces.
  • I min Mac OS X er 10 MB ung plads yderligere fordelt mellem Eden og to Survivor-rum, givet 8 MB og 2x1 MB tilsvarende. Igen er det de platformspecifikke standarder, der bruges.

Faktisk, når du starter testen og kigger under motorhjelmen med jstat , ser vi følgende, hvilket bekræfter vores skøn over bagsiden af ​​servietten:

My Precious:gc-pressure me$ jstat -gc 2533 1s
 S0C    S1C    S0U    S1U      EC       EU        OC         OU       PC     PU    YGC     YGCT    FGC    FGCT     GCT   
1024.0 1024.0  0.0    0.0    8192.0   5154.4   20480.0      0.0     21504.0 2718.9      0    0.000   0      0.000    0.000
1024.0 1024.0  0.0    0.0    8192.0   5502.1   20480.0      0.0     21504.0 2720.1      0    0.000   0      0.000    0.000
1024.0 1024.0  0.0    0.0    8192.0   6197.5   20480.0      0.0     21504.0 2721.0      0    0.000   0      0.000    0.000
1024.0 1024.0  0.0    0.0    8192.0   6545.2   20480.0      0.0     21504.0 2721.2      0    0.000   0      0.000    0.000
1024.0 1024.0  0.0    0.0    8192.0   7066.8   20480.0      0.0     21504.0 2721.6      0    0.000   0      0.000    0.000
1024.0 1024.0  0.0    0.0    8192.0   7588.3   20480.0      0.0     21504.0 2722.1      0    0.000   0      0.000    0.000

Herfra kan vi også give det næste sæt forudsigelser om, hvad der kommer til at ske:

  • De 8 MB i Eden vil blive fyldt på omkring 16 sekunder – husk, vi genererer 500 KB objekter i sekundet
  • I hvert øjeblik har vi ca. 2,5 MB levende objekter – generering af 500 KB hvert sekund og opbevaring af referencer for objekterne i fem sekunder giver os omtrent det antal
  • Minor GC vil udløses, når Eden er fuld – hvilket betyder, at vi bør se en mindre GC hvert 16. sekund eller deromkring.
  • Efter den mindre GC ender vi med en for tidlig kampagne – Survivor-pladser er kun 1 MB store, og livesættet på 2,5 MB passer ikke ind i nogen af ​​vores 1 MB Survivor-pladser. Så den eneste måde at rense Eden på er at udbrede de 1,5 MB (2,5 MB-1 MB) af levende objekter, der ikke passer ind i Survivor to Tenured-rummet.

At tjekke logfilerne giver os også tillid til disse forudsigelser:

My Precious:gc-pressure me$ jstat -gc -t 2575 1s
Time   S0C    S1C    S0U    S1U      EC       EU        OC         OU       PC     PU    YGC     YGCT    FGC    FGCT     GCT   
  6.6 1024.0 1024.0  0.0    0.0    8192.0   4117.9   20480.0      0.0     21504.0 2718.4      0    0.000   0      0.000    0.000
  7.6 1024.0 1024.0  0.0    0.0    8192.0   4639.4   20480.0      0.0     21504.0 2718.7      0    0.000   0      0.000    0.000
	... cut for brevity ...
 14.7 1024.0 1024.0  0.0    0.0    8192.0   8192.0   20480.0      0.0     21504.0 2723.6      0    0.000   0      0.000    0.000
 15.6 1024.0 1024.0  0.0   1008.0  8192.0   963.4    20480.0     1858.7   21504.0 2726.5      1    0.003   0      0.000    0.003
 16.7 1024.0 1024.0  0.0   1008.0  8192.0   1475.6   20480.0     1858.7   21504.0 2728.4      1    0.003   0      0.000    0.003
	... cut for brevity ...
 29.7 1024.0 1024.0  0.0   1008.0  8192.0   8163.4   20480.0     1858.7   21504.0 2732.3      1    0.003   0      0.000    0.003
 30.7 1024.0 1024.0 1008.0  0.0    8192.0   343.3    20480.0     3541.3   21504.0 2733.0      2    0.005   0      0.000    0.005
 31.8 1024.0 1024.0 1008.0  0.0    8192.0   952.1    20480.0     3541.3   21504.0 2733.0      2    0.005   0      0.000    0.005
	... cut for brevity ...
 45.8 1024.0 1024.0 1008.0  0.0    8192.0   8013.5   20480.0     3541.3   21504.0 2745.5      2    0.005   0      0.000    0.005
 46.8 1024.0 1024.0  0.0   1024.0  8192.0   413.4    20480.0     5201.9   21504.0 2745.5      3    0.008   0      0.000    0.008
 47.8 1024.0 1024.0  0.0   1024.0  8192.0   961.3    20480.0     5201.9   21504.0 2745.5      3    0.008   0      0.000    0.008

Ikke på 16 sekunder, men mere som hvert 15. sekund eller deromkring, starter affaldsindsamlingen, renser Eden og spreder ~1MB af levende objekter til et af Survivor-rummene og flyder resten over til Old space.

Så langt så godt. JVM'en opfører sig præcis, som vi forventer. Den interessante del starter, efter at JVM har overvåget GC-adfærden i et stykke tid og begynder at forstå, hvad der sker. Under vores testcase sker dette på omkring 90 sekunder:

My Precious:gc-pressure me$ jstat -gc -t 2575 1s
Time   S0C    S1C    S0U    S1U      EC       EU        OC         OU       PC     PU    YGC     YGCT    FGC    FGCT     GCT   
 94.0 1024.0 1024.0  0.0   1024.0  8192.0   8036.8   20480.0     8497.0   21504.0 2748.8      5    0.012   0      0.000    0.012
 95.0 1024.0 3072.0 1024.0  0.0    4096.0   353.3    20480.0    10149.6   21504.0 2748.8      6    0.014   0      0.000    0.014
 96.0 1024.0 3072.0 1024.0  0.0    4096.0   836.6    20480.0    10149.6   21504.0 2748.8      6    0.014   0      0.000    0.014
 97.0 1024.0 3072.0 1024.0  0.0    4096.0   1350.0   20480.0    10149.6   21504.0 2748.8      6    0.014   0      0.000    0.014
 98.0 1024.0 3072.0 1024.0  0.0    4096.0   1883.5   20480.0    10149.6   21504.0 2748.8      6    0.014   0      0.000    0.014
 99.0 1024.0 3072.0 1024.0  0.0    4096.0   2366.8   20480.0    10149.6   21504.0 2748.8      6    0.014   0      0.000    0.014
100.0 1024.0 3072.0 1024.0  0.0    4096.0   2890.2   20480.0    10149.6   21504.0 2748.8      6    0.014   0      0.000    0.014
101.0 1024.0 3072.0 1024.0  0.0    4096.0   3383.7   20480.0    10149.6   21504.0 2748.8      6    0.014   0      0.000    0.014
102.0 1024.0 3072.0 1024.0  0.0    4096.0   3909.7   20480.0    10149.6   21504.0 2748.8      6    0.014   0      0.000    0.014
103.0 3072.0 3072.0  0.0   2720.0  4096.0   323.0    20480.0    10269.6   21504.0 2748.9      7    0.016   0      0.000    0.016

Det, vi ser her, er JVM'ens fantastiske tilpasningsevne. Efter at have lært om applikationsadfærden har JVM ændret størrelsen på overlevelsespladsen til at være stor nok til at rumme alle levende objekter. Ny konfiguration for Young-området er nu:

  • Eden 4 MB
  • Survivor rummer 3 MB hver

Herefter stiger GC-frekvensen - Eden er nu 50 % mindre og i stedet for ~16 sekunder fylder den nu omkring 8 sekunder eller deromkring. Men fordelen er også synlig, da overlevelsesrummene nu er store nok til at rumme de levende objekter til enhver tid. Sammen med det faktum, at ingen objekter lever længere end en enkelt mindre GC-cyklus (husk, kun 2,5 MB levende objekter på et givet tidspunkt), stopper vi med at promovere objekter til det gamle rum.

Ved fortsat at overvåge JVM ser vi, at det gamle pladsforbrug er konstant efter vedtagelsen. Ikke flere objekter forplantes til gamle, men da ingen større GC udløses, vil de ~10 MB affald, der nåede at forplante sig, før tilpasningen fandt sted, leve i det gamle rum for evigt.

Du kan også slå den "fantastiske tilpasningsevne" fra, hvis du er sikker på, hvad du laver. Angivelse af -XX-UseAdaptiveSizingPolicy i dine JVM-parametre vil instruere JVM om at holde sig til de parametre, der blev givet ved lanceringen og ikke forsøge at overliste dig. Brug denne mulighed med omhu, moderne JVM'er er generelt rigtig gode til at forudsige den passende konfiguration for dig.

Java tag