2016-07-01 30 views
2

Wir haben eine einfache Microservice-Setup, basierend auf Spring Boot und Java 8 auf Windows-Servern.Freigabe von Speicher zurück zu OS, wenn JVM im Leerlauf ist

Viele der Dienste haben eine geringe Auslastung, da sie als Integration für alle Arten von externen Partnern dienen. Sie sind also oft untätig.

Das Problem ist, dass die JVM Speicher nur zurück zum Betriebssystem freigibt, wenn eine Speicherbereinigung ausgelöst wird. So könnte ein Dienst beginnen, 32 MB zu verwenden, dann eine einzelne Anfrage zu bedienen und 2 GB Speicher zuzuweisen. Wenn es keine andere Aktivität für diesen Dienst gibt, werden GC und andere Dienste auf dem Server nicht beeinträchtigt.

einen GC extern oder intern mit einem System.gc Triggern funktionieren gut und ich habe herausgefunden, wie -XX:MaxHeapFreeRatio und -XX:MinHeapFreeRatio mit -XX:+UseG1GC verwenden, um zu steuern, wenn der Heap sollte Speicher an das Betriebssystem erweitern und lösen.

Meine Frage ist: Was ist der beste Weg, um sicherzustellen, dass Speicher zurück an das Betriebssystem relased wird, wenn die JVM im Leerlauf ist?

Eine Idee wäre, den Dienst überwachen zu lassen und eine System.gc nach einer Periode der Untätigkeit auszulösen, aber das könnte schwierig und fehlerträchtig sein. Ich hoffe auf bessere Vorschläge.

Sie können reproduzieren, indem Sie X-Instanzen dieses Programms ausführen. Ungefähr 10 haben meine Windows-Maschine mit 8GB aufgegeben.

import java.util.*; 

public class Load { 
    public static void main(String[] args) throws Exception { 
    alloc(); 
    Scanner s = new Scanner(System.in); 
    System.out.println("enter to gc "); 
    s.nextLine(); 
    System.gc(); 
    System.out.println("enter to exit"); 
    s.nextLine(); 
    } 

    private static void alloc() { 
    ArrayList<String[]> strings = new ArrayList<>(); 
    int max = 1000000; 
    for (int i = 0; i < max; i++) { 
     strings.add(new String[500]); 
    } 
    } 
} 

c:\> java -server -XX:+UseG1GC -Xms32m -Xmx2048m Load

Edit: Dies als Duplikat zweimal markiert wurde, aber es ist kein Duplikat der verknüpften Fragen. Die erste Frage ist eine Version 2010 der gleichen Frage, aber diese Frage bezieht sich darauf, warum der GC Speicher nicht an das Betriebssystem zurückgibt (was zu dieser Zeit nicht möglich war). Die andere Frage betrifft grundlegende GC-Einstellungen, die ich bereits geschrieben habe. Ich wünsche eine Diskussion darüber, wie der Garbage Collector ausgelöst wird, wenn das System im Leerlauf ist. Es ist daher nicht akzeptabel, System.gc alle fünf Sekunden auszuführen, da dies ein hohes Risiko birgt, mit gültigen Anforderungen zu kollidieren und die Antwortzeiten zu ruinieren.

+0

Dies ist wahrscheinlich ein Duplikat von http://stackoverflow.com/questions/4952568/is-there-a-way-to-lower -java-heap-while-not-in-use auch, ruft 'System.gc()' nicht garantiert, dass GC an diesem Punkt initiiert wird, siehe http://stackoverflow.com/questions/66540/when- does-system-gc-do-anything –

+0

Meine Frage ist kein Duplikat dieser Frage. Ich verstehe die grundlegenden Mechanismen von GC und wie Heap dem Betriebssystem zurückgegeben wird. Ich verstehe auch, dass das System.gc ohne Garantien kommt, obwohl ich es sehen muss, eine Anfrage zu ignorieren. Ich möchte eine Diskussion darüber, wie explizite Garbage Collections am besten aufrufen, wenn das System inaktiv ist. –

+0

Entschuldigung, ich scheine den eigentlichen Punkt Ihres Posts verpasst zu haben "Meine Frage ist: Was ist der beste Weg, um sicherzustellen, dass der Speicher wieder an das Betriebssystem zurückgegeben wird, wenn die JVM im Leerlauf ist?" –

Antwort

1

Wenn der Aufruf von System.gc() Ihre Anforderungen erfüllt, würde ich empfehlen, Spring-Scheduler zu verwenden, um eine periodische Aufgabe jedes x Sedonds auszuführen.

Das ist ganz einfach zu implementieren, einige Anmerkungen

@EnableAsync 
@EnableScheduling 
@Scheduled(cron = "...") 

ist alles, was Sie brauchen. Weitere Informationen finden Sie unter spring scheduling.

bearbeiten

Aufruf System.gc() gibt schlägt vor, nur die Garbage Collection zu starten, die immer noch auf der JVM zu entscheiden, wann es zu tun oder nicht.

Um herauszufinden, ob Ihr System im Leerlauf ist oder nicht, können Sie die Federkennzahlen verwenden. Es gibt einige Unterklassen

org.springframework.boot.actuate.endpoint.PublicMetrics 

wie TomcatPublicMetrics oder SystemPublicMetrics, die Sie Informationen über das System geben. Sie können sie mit @Autowire injizieren und mertics() aufrufen, um einzelne Werte zu erhalten.Basierend darauf können Sie möglicherweise entscheiden, ob Ihr System im Leerlauf ist oder nicht,

+0

Nun, ja. Etwas allein diese Linien ist, was ich denke. Aber ich denke auch, dass eine Lösung wie diese auch unerwünschte Speicherbereinigungen auslösen würde, wenn das System NICHT im Leerlauf ist. Z.B. einen vollständigen Heap-GC in der Mitte einer Anfrage auszulösen, wird wahrscheinlich mein SLA durch viele lange Antworten, die durch schlecht geplante GC verursacht wurden, ruinieren. –

+0

Akzeptieren dieser Antwort. Es fühlt sich einfach an wie ein Hack. Ich hätte eine externe Lösung bevorzugt, wie eine clevere JVM-Flagge oder etwas anderes. –