2016-03-10 6 views
15

Seit Jahren führen wir Java-Dienste mit bescheidenen Heap-Größen unter Verwendung von +UseParallelOldGC. Jetzt beginnen wir mit der Einführung eines neuen Service mit einem größeren Heap und dem G1-Collector. Das läuft ziemlich gut.Java G1: Überwachung auf Speicherlecks in der Produktion

Für unsere Dienste, die +UseParallelOldGC verwenden, überwachen wir auf Speicherlecks, indem wir die alte Generationsgröße nach der Erfassung betrachten und auf einen Schwellenwert aufmerksam machen. Das funktioniert ganz gut und hat unseren Speck erst vor zwei Wochen gerettet.

Speziell für +UseParallelOldGC, tun wir folgendes:

  • ManagementFactory.getMemoryPoolMXBeans()
  • Suche nach dem MemoryPoolMXBean Ergebnis mit dem Namen
  • Vergleichen getCollectionUsage().getUsed() (falls vorhanden) mit getMax()
in "Old Gen" endend

Leider scheint G1 kein Konzept mehr von 012 zu haben.

Grundsätzlich möchten wir jedoch die G1-Heap-Größe nach der letzten gemischten Sammlung in einem gemischten Zyklus oder etwas Ähnlichem überwachen.

Zum Beispiel außerhalb des VM ich mit einem awk-Skript glücklich sein würde, die lediglich die letzten '(mixed)' wurde gefunden, dass durch ein '(young)' gefolgt ist und schauen, was die endgültige Heap-Größe war (zB '1540.0M' 'Heap: 3694.5M(9216.0M)->1540.0M(9216.0M)')

Gibt es ein Möglichkeit, dies innerhalb der Java VM zu tun?

+0

Dies sollte ähnliche Informationen bereitstellen: http://StackOverflow.com/a/ 32509813/1362755 – the8472

Antwort

2

Ja, JVM bietet Ihnen genug Tools, um solche Informationen für G1 abzurufen. Zum Beispiel könnten Sie so etwas wie diese Klasse verwenden, die alle Details über Garbage Collection druckt (nur MemoryUtil.startGCMonitor() nennen):

public class MemoryUtil { 

    private static final Set<String> heapRegions; 

    static { 
     heapRegions = ManagementFactory.getMemoryPoolMXBeans().stream() 
       .filter(b -> b.getType() == MemoryType.HEAP) 
       .map(MemoryPoolMXBean::getName) 
       .collect(Collectors.toSet()); 
    } 

    private static NotificationListener gcHandler = (notification, handback) -> { 
     if (notification.getType().equals(GarbageCollectionNotificationInfo.GARBAGE_COLLECTION_NOTIFICATION)) { 
      GarbageCollectionNotificationInfo gcInfo = GarbageCollectionNotificationInfo.from((CompositeData) notification.getUserData()); 
      Map<String, MemoryUsage> memBefore = gcInfo.getGcInfo().getMemoryUsageBeforeGc(); 
      Map<String, MemoryUsage> memAfter = gcInfo.getGcInfo().getMemoryUsageAfterGc(); 
      StringBuilder sb = new StringBuilder(250); 
      sb.append("[").append(gcInfo.getGcAction()).append("/").append(gcInfo.getGcCause()) 
        .append("/").append(gcInfo.getGcName()).append("/("); 
      appendMemUsage(sb, memBefore); 
      sb.append(") -> ("); 
      appendMemUsage(sb, memAfter); 
      sb.append("), ").append(gcInfo.getGcInfo().getDuration()).append(" ms]"); 
      System.out.println(sb.toString()); 
     } 
    }; 

    public static void startGCMonitor() { 
     for(GarbageCollectorMXBean mBean: ManagementFactory.getGarbageCollectorMXBeans()) { 
      ((NotificationEmitter) mBean).addNotificationListener(gcHandler, null, null); 
     } 
    } 

    public static void stopGCMonitor() { 
     for(GarbageCollectorMXBean mBean: ManagementFactory.getGarbageCollectorMXBeans()) { 
      try { 
       ((NotificationEmitter) mBean).removeNotificationListener(gcHandler); 
      } catch(ListenerNotFoundException e) { 
       // Do nothing 
      } 
     } 
    } 

    private static void appendMemUsage(StringBuilder sb, Map<String, MemoryUsage> memUsage) { 
     memUsage.entrySet().forEach((entry) -> { 
      if (heapRegions.contains(entry.getKey())) { 
       sb.append(entry.getKey()).append(" used=").append(entry.getValue().getUsed() >> 10).append("K; "); 
      } 
     }); 
    } 
} 

In diesem Code gcInfo.getGcAction() Sie genügend Informationen gibt von großen/mixed diejenigen kleinere Sammlungen zu trennen.

Aber es gibt einen wichtigen Vorbehalt bei der Verwendung Ihres Ansatzes (mit einem Schwellenwert) für G1. Eine einzelne gemischte Sammlung in G1 betrifft normalerweise nur einige alte Genregionen - viele genug, um genügend Speicher freizugeben, aber nicht zu viele, um die GC-Pause niedrig zu halten. Nach einer gemischten Sammlung in G1 können Sie also nicht sicher sein, dass all Ihr Müll weg ist. Als Ergebnis müssen Sie eine ausgeklügeltere Strategie zur Erkennung von Speicherlecks finden (möglicherweise basierend auf der Häufigkeit der Sammlungen, Sammeln von Statistiken aus verschiedenen Sammlungen usw.).