garbage-collectionAan de slag met garbage collection


Opmerkingen

Garbage Collection (GC) is een manier om automatisch geheugen terug te winnen dat wordt ingenomen door objecten die niet langer nodig zijn voor een programma. Dit in tegenstelling tot handmatig geheugenbeheer, waarbij het programmeerapparaat expliciet opgeeft welke objecten moeten worden toegewezen en naar het geheugen moeten worden teruggestuurd. Goede GC-strategieën kunnen efficiënter zijn dan handmatig geheugenbeheer, maar dit kan afhangen van het type software.

De belangrijkste voordelen van afvalinzameling zijn:

  • Het bevrijdt de programmeur van handmatig geheugenbeheer.
  • Het vermijdt bepaalde moeilijk te vinden bugs die kunnen voortvloeien uit handmatig geheugenbeheer (bijv. Bungelende wijzers, dubbele ontgrendeling, bepaalde soorten geheugenlekken).
  • Talen die afvalinzameling gebruiken, zijn meestal minder complex.

De belangrijkste nadelen zijn:

  • Garbage collection heeft wat overhead in vergelijking met handmatig geheugenbeheer.
  • Het kan de prestaties beïnvloeden, vooral wanneer het ophalen van afval op ongewenste momenten wordt geactiveerd.
  • Het is niet bepaald, de programmeur weet niet wanneer de afvalinzameling is voltooid en of objecten worden vrijgegeven of niet.

De meeste 'nieuwere' programmeertalen hebben ingebouwde garbage collection, bijvoorbeeld Java, C #, .NET, Ruby en JavaScript. Oudere talen zoals C en C ++ hebben geen garbage collection hoewel er implementaties beschikbaar zijn met garbage collection. Er zijn ook talen waarin je een combinatie van afvalinzameling en handmatig geheugenbeheer kunt gebruiken, bijvoorbeeld Modula-3 en Ada.

Garbage collection strategieën verschillen, maar velen gebruiken een (variatie van) de mark-and-sweep-aanpak. In de markeringsfase worden alle toegankelijke objecten gevonden en gemarkeerd. In de sweep-fase wordt de heap gescand op ontoegankelijke en ongemarkeerde objecten die vervolgens worden opgeruimd. Moderne afvalverzamelaars gebruiken ook een generatieaanpak waarbij twee of meer objecttoewijzingsregio's (generaties) worden behouden. De jongste generatie bevat de nieuwste toegewezen objecten en wordt vaker schoongemaakt. Objecten die gedurende een bepaalde tijdspanne 'overleven' worden gepromoveerd tot een oudere generatie.

In veel talen met GC kunnen programmeurs het verfijnen (zie bijvoorbeeld de Java 8 Virtual Machine Garbage Collection Tuning Guide of de .Net Garbage Collection-documentatie )

Uitgebreide gc-logging inschakelen in Java

Normaal is de garbage collection (gc) van jvm transparant voor de gebruiker (ontwikkelaar / ingenieur).

GC-afstemming is normaal niet vereist, tenzij de gebruiker geconfronteerd wordt met een geheugenlek of een toepassing heeft die veel geheugen vereist - beide leiden uiteindelijk tot een uitzondering wegens onvoldoende geheugen die de gebruiker ertoe dwingt het probleem te onderzoeken.

De eerste stap is meestal om het geheugen te vergroten (ofwel de heap of de perm-gen / meta-ruimte, afhankelijk van of het te wijten is aan belasting tijdens runtime of de bibliotheekbasis van de applicatie groot is of er is een lek in de classloading of thread afhandelingsmechanisme). Maar wanneer dat niet haalbaar is, is de volgende stap proberen te begrijpen wat er misgaat.

Als je alleen de momentopname op een bepaald moment in de tijd wilt, jstat het jstat hulpprogramma dat deel uitmaakt van de jdk.

Voor een meer gedetailleerd begrip is het echter handig om een logboek te hebben met de momentopname van de heap voor en na elke gc-gebeurtenis. Daarvoor moet de gebruiker uitgebreide gc-logging inschakelen met behulp van de -verbose:gc als onderdeel van de opstartparameters van -XX:+PrintGCDetails en inclusief -XX:+PrintGCDetails en -XX:+PrintGCTimeStamp vlaggen.

Voor degenen die hun applicatie proactief willen profileren, zijn er ook tools zoals jvisualvm die ook deel uitmaken van de jdk waarmee ze inzicht kunnen krijgen in het gedrag van applicaties.

Hieronder vindt u een voorbeeldprogramma, de gc-configuratie en de uitvoer van het verbose-gc-logboek:

package com.example.so.docs.gc.logging;

import java.util.Arrays;
import java.util.Random;

public class HelloWorld {

    public static void main(String[] args) {
        sortTest();
    }
    
    private static void sortTest() {
        System.out.println("HelloWorld");
        
        int count = 3;
        while(count-- > 0) {
            int size = 1024*1024;
            int[] numbers = new int[size];
            Random random = new Random();
            for(int i=0;i<size;i++) {
                numbers[i] = random.nextInt(size);
            }
            
            Arrays.sort(numbers);
        }
        System.out.println("Done");
        
    }
    

}
 

GC-opties:

-server -verbose:gc  -XX:+PrintGCDetails -XX:+PrintGCTimeStamps  -Xmx10m  -XX:-PrintTenuringDistribution  -XX:MaxGCPauseMillis=250 -Xloggc:/path/to/logs/verbose_gc.log
 

Uitgang:

Java HotSpot(TM) 64-Bit Server VM (25.72-b15) for windows-amd64 JRE (1.8.0_72-b15), built on Dec 22 2015 19:16:16 by "java_re" with MS VC++ 10.0 (VS2010)
Memory: 4k page, physical 6084464k(2584100k free), swap 8130628k(3993460k free)
CommandLine flags: -XX:InitialHeapSize=10485760 -XX:MaxGCPauseMillis=250 -XX:MaxHeapSize=10485760 -XX:+PrintGC -XX:+PrintGCDetails -XX:+PrintGCTimeStamps -XX:-PrintTenuringDistribution -XX:+UseCompressedClassPointers -XX:+UseCompressedOops -XX:-UseLargePagesIndividualAllocation -XX:+UseParallelGC 
0.398: [GC (Allocation Failure) [PSYoungGen: 483K->432K(2560K)] 4579K->4536K(9728K), 0.0012569 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
0.400: [GC (Allocation Failure) [PSYoungGen: 432K->336K(2560K)] 4536K->4440K(9728K), 0.0008121 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
0.401: [Full GC (Allocation Failure) [PSYoungGen: 336K->0K(2560K)] [ParOldGen: 4104K->294K(5632K)] 4440K->294K(8192K), [Metaspace: 2616K->2616K(1056768K)], 0.0056202 secs] [Times: user=0.00 sys=0.00, real=0.01 secs] 
0.555: [GC (Allocation Failure) [PSYoungGen: 41K->0K(2560K)] 4431K->4390K(9728K), 0.0004678 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
0.555: [GC (Allocation Failure) [PSYoungGen: 0K->0K(2560K)] 4390K->4390K(9728K), 0.0003490 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
0.556: [Full GC (Allocation Failure) [PSYoungGen: 0K->0K(2560K)] [ParOldGen: 4390K->293K(5632K)] 4390K->293K(8192K), [Metaspace: 2619K->2619K(1056768K)], 0.0060187 secs] [Times: user=0.00 sys=0.00, real=0.01 secs] 
Heap
 PSYoungGen      total 2560K, used 82K [0x00000000ffd00000, 0x0000000100000000, 0x0000000100000000)
  eden space 2048K, 4% used [0x00000000ffd00000,0x00000000ffd14938,0x00000000fff00000)
  from space 512K, 0% used [0x00000000fff80000,0x00000000fff80000,0x0000000100000000)
  to   space 512K, 0% used [0x00000000fff00000,0x00000000fff00000,0x00000000fff80000)
 ParOldGen       total 5632K, used 4389K [0x00000000ff600000, 0x00000000ffb80000, 0x00000000ffd00000)
  object space 5632K, 77% used [0x00000000ff600000,0x00000000ffa49670,0x00000000ffb80000)
 Metaspace       used 2625K, capacity 4486K, committed 4864K, reserved 1056768K
  class space    used 282K, capacity 386K, committed 512K, reserved 1048576K
 

Hieronder staan enkele nuttige links op GC:

  1. Een gearchiveerde pagina met uitleg over gc-concepten (jdk7)
  2. G1 Collector-zelfstudie
  3. Handige VM-opties
  4. JDK 5 - GC Ergonomie (concepten zijn nog steeds relevant)
  5. JDK 6 Tuning (concepten zijn nog steeds relevant)

Invoering

Objecten komen in aanmerking voor garbage collection (GC) als ze niet langer bereikbaar zijn via de belangrijkste toegangspunten in een programma. GC wordt meestal niet expliciet door de gebruiker uitgevoerd, maar om de GC te laten weten dat een object niet langer nodig is, kan een ontwikkelaar:

Dereference / nul toewijzen

someFunction {
     var a = 1;
     var b = 2;
     a = null; // GC can now free the memory used for variable a
     ...
} // local variable b not dereferenced but will be subject to GC when function ends
 

Gebruik zwakke referenties

Met de meeste talen met GC kunt u zwakke verwijzingen naar een object maken die niet als referentie voor de GC gelden. Als er alleen zwakke verwijzingen naar een object zijn en geen sterke (normale) verwijzingen, komt het object in aanmerking voor GC.

WeakReference wr = new WeakReference(createSomeObject());
 

Merk op dat het na deze code gevaarlijk is om het doel van de zwakke referentie te gebruiken zonder te controleren of het object nog steeds bestaat. Beginners-programmeurs maken soms de fout om code zoals deze te gebruiken:

if wr.target is not null {
    doSomeAction(wr.target);
}
 

Dit kan problemen veroorzaken, omdat GC mogelijk is aangeroepen na de nulcontrole en vóór de uitvoering van doSomeAction. Het is beter om eerst een (tijdelijke) sterke verwijzing naar het object als volgt te maken:

Object strongRef = wr.target;
if strongRef is not null {
    doSomeAction(strongRef);
}
strongRef = null;