Vejledning til System.gc ()

1. Oversigt

I denne vejledning skal vi undersøge System.gc () metode placeret i java.lang pakke.

Eksplicit ringer System.gc () er kendt for at være en dårlig praksis. Lad os prøve at forstå, hvorfor og hvis der er brugssager, når man kalder denne metode, kan det være nyttigt.

2. Affaldssamling

Java Virtual Machine beslutter at udføre affaldsindsamling, når der er indikationer for at gøre det. Disse indikationer adskiller sig fra en GC-implementering til en anden. De er baseret på forskellige heuristikker. Der er dog nogle få øjeblikke, hvor GC vil blive udført med sikkerhed:

  • Den nye generation (Tenured space) er fuld, hvilket udløser mindre GC
  • Den gamle generation (Eden + Survivor0 + Survivor1 mellemrum) er fuld, hvilket udløser større / fuld GC

Det eneste, der er uafhængig af GC-implementeringen, er genstandsberettigelse til at blive indsamlet skrald.

Nu skal vi se på System.gc () selve metoden.

3. System.gc ()

En påkaldelse af metoden er enkel:

System.gc ()

Den officielle Oracle-dokumentation siger, at:

Ringer til gc metode foreslår at Java Virtual Machine bruger en indsats på at genbruge ubrugte objekter for at gøre den hukommelse, de i øjeblikket optager, tilgængelig til hurtig genbrug.

Der er ingen garanti for, at den faktiske GC udløses.

System.gc () udløser en større GC. Derfor er der en risiko for at bruge lidt tid på stop-the-world-fasen afhængigt af din affaldssamlerimplementering. Som resultat, vi har et upålideligt værktøj med en potentielt betydelig præstationsstraff.

Eksistensen af ​​eksplicit affaldsindsamling skal være et seriøst rødt flag for alle.

Vi kan forhindre System.gc () fra at udføre noget arbejde ved hjælp af -XX: DisableExplicitGC JVM-flag.

3.1. Performance Tuning

Det er værd at bemærke, at lige før du smider en OutOfMemoryError, JVM'en udfører en fuld GC. Derfor et eksplicit opkald til System.gc () vil ikke redde os fra fiasko.

Affaldssamlere i dag er virkelig smarte. De har al viden om hukommelsesforbrug og andre statistikker for at kunne træffe passende beslutninger. Derfor bør vi stole på dem.

I tilfælde af hukommelsesproblemer har vi en masse indstillinger, som vi kan ændre for at indstille vores applikation - startende fra at vælge en anden affaldssamler, gennem indstilling af ønsket applikationstid / GC-tidsforhold og endelig med at indstille faste størrelser for hukommelsessegmenter.

Der er også måder at afbøde virkningerne af Full GC forårsaget af et eksplicit opkald. Vi kan bruge et af flagene:

-XX: + EksplicitGCInvokesConcurrent

eller:

-XX: + EksplicitGCInvokesConcurrentAndUnloadsClasses

Hvis vi virkelig vil have, at vores app fungerer korrekt, skal vi løse det virkelige underliggende hukommelsesproblem.

I det næste kapitel ser vi et praktisk eksempel, når vi udtrykkeligt ringer System.gc () synes at være nyttigt.

4. Brugseksempel

4.1. Scenarie

Lad os skrive en testapp. Vi ønsker at finde en situation, når vi ringer System.gc () kan være nyttigt.

Mindre affaldsindsamling sker oftere end den store. Så vi skal nok fokusere på sidstnævnte. Et enkelt objekt flyttes til fast ejendom, hvis det "overlevede" nogle få samlinger og stadig kan nås fra GC-rødder.

Lad os forestille os, at vi har en enorm samling af objekter, der lever i nogen tid. Derefter rydder vi på et tidspunkt samlingen af ​​objekter. Måske er det et godt øjeblik at løbe System.gc ()?

4.2. Demo ansøgning

Vi opretter en simpel konsolapp, der giver os mulighed for at simulere dette scenario:

offentlig klasse DemoApplication {privat statisk endelig Map cache = ny HashMap (); public static void main (String [] args) {Scannerscanner = ny scanner (System.in); mens (scanner.hasNext ()) {final String next = scanner.next (); hvis ("udfyld". er lig med (næste)) {for (int i = 0; i <1000000; i ++) {cache.put (randomUUID (). toString (), randomUUID (). toString ()); }} ellers hvis ("ugyldiggør" .equals (næste)) {cache.clear (); } ellers hvis ("gc" .equals (næste)) {System.gc (); } ellers hvis ("exit" .equals (næste)) {System.exit (0); } andet {System.out.println ("ukendt"); }}}}

4.3. Kører demonstrationen

Lad os køre vores applikation med et par ekstra flag:

-XX: + PrintGCDetails -Xloggc: gclog.log -Xms100M -Xmx500M -XX: + UseConcMarkSweepGC

De første to flag er nødvendige for at logge GC-information. De næste to flag angiver den indledende bunke størrelse og derefter den maksimale bunke størrelse. Vi vil holde dyngestørrelsen lav for at tvinge GC til at være mere aktiv. Endelig beslutter vi at bruge CMS - Concurrent Mark and Sweep garbage collector. Det er tid til at køre vores app!

Lad os først prøv at udfylde fast ejendom. Type fylde.

Vi kan undersøge vores gclog.log fil for at se, hvad der skete. Vi ser omkring 15 samlinger. Linjen, der er logget på for enkelte samlinger, ser ud som:

197.057: [GC (Allocation Failure) 197.057: [ParNew: 67498K-> 40K (75840K), 0.0016945 secs] 168754K-> 101295K (244192K), 0.0017865 secs] [Times: user = 0.01 sys = 0.00, real = 0.00 secs] sekunder]

Som vi kan se, er hukommelsen fyldt.

Lad os derefter kraft System.gc () ved at skrive gc. Vi kan se, at hukommelsesforbruget ikke ændrede sig væsentligt:

238.810: [Fuld GC (System.gc ()) 238.810: [CMS: 101255K-> 101231K (168352K); 0,2634318 sek.] 120693K-> 101231K (244192K), [Metaspace: 32186K-> 32186K (1079296K)], 0,2635908 sek.] [Times: bruger = 0,27 sys = 0,00, real = 0,26 sek]

Efter et par flere kørsler ser vi, at hukommelsesstørrelsen forbliver på samme niveau.

Lad os ryd cachen ved at skrive ugyldiggør. Vi skal ikke se flere loglinjer vises i gclog.log fil.

Vi kan prøve at udfylde cachen et par gange mere, men der sker ingen GC. Dette er et øjeblik, hvor vi kan overvinde skraldespanden. Efter at have tvunget GC, ser vi en linje som:

262.124: [Fuld GC (System.gc ()) 262.124: [CMS: 101523K-> 14122K (169324K); 0,0975656 sek.] 103369K-> 14122K (245612K), [Metaspace: 32203K-> 32203K (1079296K)], 0,0977279 sek.] [Gange: bruger = 0,10 sys = 0,00, real = 0,10 sek]

Vi har frigivet en imponerende mængde hukommelse! Men var det virkelig nødvendigt lige nu? Hvad skete der?

Ifølge dette eksempel ringer System.gc () kan virke fristende, når vi frigiver store genstande eller annullerer cacher.

5. Andre anvendelser

Der er meget få grunde til, at et eksplicit opkald til System.gc () metode kan være nyttig.

En mulig årsag er rengøring af hukommelse efter serverstart - vi starter en server eller applikation, der gør en masse forberedelser. Derefter er der mange objekter, der skal færdiggøres. Rengøring efter en sådan forberedelse bør dog ikke være vores ansvar.

En anden er analyse af hukommelseslækagedet er mere en debugging-praksis end noget, vi gerne vil have i produktionskoden. Ringer System.gc () og at se bunkeplads stadig er høj kan være en indikation af en hukommelseslækage.

6. Resume

I denne artikel undersøgte vi System.gc () metode, og hvornår det kan synes nyttigt.

Vi bør aldrig stole på det, når det kommer til rigtigheden af ​​vores app. GC er i de fleste tilfælde smartere end os, og i tilfælde af hukommelsesproblemer bør vi overveje at indstille den virtuelle maskine i stedet for at foretage et så eksplicit opkald.

Som sædvanlig kan koden, der bruges i denne artikel, findes på GitHub.