Gids voor System.gc ()

1. Overzicht

In deze tutorial gaan we de Systeem.gc () methode in de java.lang pakket.

Expliciet bellen Systeem.gc () staat bekend als een slechte gewoonte. Laten we proberen te begrijpen waarom en of er use-cases zijn wanneer het aanroepen van deze methode nuttig kan zijn.

2. Garbagecollection

De Java Virtual Machine besluit om garbage collection uit te voeren wanneer daar aanwijzingen voor zijn. Die indicaties verschillen van de ene GC-implementatie tot de andere. Ze zijn gebaseerd op verschillende heuristieken. Er zijn echter een paar momenten waarop GC zeker wordt uitgevoerd:

  • De nieuwe generatie (Tenured space) is vol, waardoor een kleine GC wordt geactiveerd
  • De oude generatie (Eden + Survivor0 + Survivor1-velden) is vol, waardoor de grote / volledige GC wordt geactiveerd

Het enige dat onafhankelijk is van de GC-implementatie, is dat het object in aanmerking komt voor garbagecollection.

Nu zullen we de Systeem.gc () methode zelf.

3. Systeem.gc ()

Het aanroepen van de methode is eenvoudig:

Systeem.gc ()

In de officiële Oracle-documentatie staat dat:

Bellen met het gc methode suggereert dat de Java Virtual Machine moeite doet om ongebruikte objecten te recyclen om het geheugen dat ze momenteel innemen beschikbaar te maken voor snel hergebruik.

Er is geen garantie dat de daadwerkelijke GC wordt geactiveerd.

Systeem.gc () veroorzaakt een grote GC. Daarom bestaat het risico dat u wat tijd besteedt aan de stop-de-wereld-fase, afhankelijk van de implementatie van uw garbage collector. Als resultaat, we hebben een onbetrouwbare tool met een mogelijk aanzienlijke prestatieverlies.

Het bestaan ​​van een expliciete aanroep van garbage collection zou voor iedereen een serieuze rode vlag moeten zijn.

We kunnen voorkomen Systeem.gc () van enig werk met behulp van de -XX: DisableExplicitGC JVM-vlag.

3.1. Prestaties afstemmen

Het is vermeldenswaard dat vlak voordat u een Onvoldoende geheugen fout, de JVM zal een volledige GC uitvoeren. Daarom een ​​expliciete oproep aan Systeem.gc () zal ons niet redden van mislukking.

Vuilnismannen zijn tegenwoordig erg slim. Ze hebben alle kennis over geheugengebruik en andere statistieken om de juiste beslissingen te kunnen nemen. Daarom moeten we ze vertrouwen.

In het geval van geheugenproblemen, hebben we een aantal instellingen die we kunnen wijzigen om onze applicatie af te stemmen - beginnend bij het kiezen van een andere garbage collector, door het instellen van de gewenste applicatietijd / GC-tijdverhouding, en tot slot, eindigend met het instellen van vaste grootten voor geheugensegmenten.

Er zijn ook manieren om de effecten van Full GC, veroorzaakt door een expliciete aanroep, te verzachten. We kunnen een van de vlaggen gebruiken:

-XX: + ExplicitGCInvokesConcurrent

of:

-XX: + ExplicitGCInvokesConcurrentAndUnloadsClasses

Als we echt willen dat onze app goed werkt, moeten we het echte onderliggende geheugenprobleem oplossen.

In het volgende hoofdstuk zullen we een praktisch voorbeeld zien wanneer we expliciet aanroepen Systeem.gc () lijkt nuttig te zijn.

4. Gebruiksvoorbeeld

4.1. Scenario

Laten we een test-app schrijven. We willen een situatie vinden wanneer we bellen Systeem.gc () kan nuttig zijn.

Kleine garbagecollection komt vaker voor dan de grote. Dus we moeten ons waarschijnlijk concentreren op het laatste. Een enkel object wordt naar een vaste ruimte verplaatst als het een paar verzamelingen heeft 'overleefd' en nog steeds bereikbaar is vanaf GC-wortels.

Laten we ons voorstellen dat we een enorme verzameling objecten hebben die al een tijdje leven. Dan, op een gegeven moment, ruimen we de verzameling objecten op. Misschien is het een goed moment om te rennen Systeem.gc ()?

4.2. Demo-applicatie

We zullen een eenvoudige console-app maken waarmee we dat scenario kunnen simuleren:

openbare klasse DemoApplication {privé statische definitieve kaartcache = nieuwe HashMap (); public static void main (String [] args) {Scanner scanner = nieuwe scanner (System.in); while (scanner.hasNext ()) {final String next = scanner.next (); if ("fill" .equals (next)) {for (int i = 0; i <1000000; i ++) {cache.put (randomUUID (). toString (), randomUUID (). toString ()); }} else if ("invalidate" .equals (volgende)) {cache.clear (); } else if ("gc" .equals (volgende)) {System.gc (); } else if ("exit" .equals (next)) {System.exit (0); } else {System.out.println ("onbekend"); }}}}

4.3. De demo uitvoeren

Laten we onze applicatie uitvoeren met een paar extra vlaggen:

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

De eerste twee vlaggen zijn nodig om GC-informatie te loggen. De volgende twee vlaggen stellen de initiële heap-grootte in en vervolgens de maximale heap-grootte. We willen de heap-grootte laag houden om GC te dwingen actiever te zijn. Ten slotte besluiten we om CMS - Concurrent Mark and Sweep garbage collector te gebruiken. Het is tijd om onze app te gebruiken!

Laten we eerst probeer vaste ruimte te vullen. Type vullen.

We kunnen ons onderzoeken gclog.log bestand om te zien wat er is gebeurd. We zullen ongeveer 15 collecties zien. De regel die is gelogd voor enkele collecties ziet er als volgt uit:

197.057: [GC (toewijzingsfout) 197.057: [ParNew: 67498K-> 40K (75840K), 0,0016945 seconden] 168754K-> 101295K (244192K), 0,0017865 seconden] [Tijden: gebruiker = 0,01 sys = 0,00, reëel = 0,00 seconden] seconden]

Zoals we kunnen zien, is het geheugen gevuld.

Laten we het volgende doen dwingen Systeem.gc () door te typen gc. We kunnen zien dat het geheugengebruik niet significant is veranderd:

238.810: [Volledige GC (System.gc ()) 238.810: [CMS: 101255K-> 101231K (168352K); 0,2634318 sec] 120693K-> 101231K (244192K), [Metaspace: 32186K-> 32186K (1079296K)], 0,2635908 sec] [Times: user = 0,27 sys = 0,00, reëel = 0,26 sec]

Na nog een paar runs, zullen we zien dat de geheugengrootte op hetzelfde niveau blijft.

Laten we wis de cache door te typen ongeldig maken. We zouden geen logboekregels meer moeten zien verschijnen in het gclog.log het dossier.

We kunnen nog een paar keer proberen de cache te vullen, maar er vindt geen GC plaats. Dit is een moment waarop we kunnen de vuilnisman te slim af zijn. Nu, na het forceren van GC, zien we een regel als:

262.124: [Volledige GC (System.gc ()) 262.124: [CMS: 101523K-> 14122K (169324K); 0,0975656 seconden] 103369K-> 14122K (245612K), [Metaspace: 32203K-> 32203K (1079296K)], 0,0977279 seconden] [Tijden: gebruiker = 0,10 sys = 0,00, reëel = 0,10 seconden]

We hebben een indrukwekkende hoeveelheid geheugen vrijgegeven! Maar was het nu echt nodig? Wat is er gebeurd?

Volgens dit voorbeeld bellen Systeem.gc () lijkt misschien verleidelijk als we grote objecten vrijgeven of caches ongeldig maken.

5. Andere toepassingen

Er zijn zeer weinig redenen waarom een ​​expliciete aanroep naar het Systeem.gc () methode kan nuttig zijn.

Een mogelijke reden is geheugen opschonen na het opstarten van de server - we starten een server of applicatie die veel voorbereiding vergt. Daarna zijn er veel objecten die moeten worden afgerond. Het schoonmaken na een dergelijke voorbereiding zou echter niet onze verantwoordelijkheid moeten zijn.

Een ander is geheugenlekanalysehet is meer een foutopsporingspraktijk dan iets dat we graag in de productiecode zouden willen behouden. Roeping Systeem.gc () en zien dat de heapruimte nog steeds hoog is, kan een aanwijzing zijn voor een geheugenlek.

6. Samenvatting

In dit artikel hebben we de Systeem.gc () methode en wanneer het nuttig lijkt.

We mogen er nooit op vertrouwen als het gaat om de juistheid van onze app. GC is in de meeste gevallen slimmer dan wij, en in geval van geheugenproblemen zouden we moeten overwegen om de virtuele machine af te stemmen in plaats van zo'n expliciete oproep te doen.

Zoals gewoonlijk is de code die in dit artikel wordt gebruikt, te vinden op GitHub.