2015-01-10 5 views
3

Я пытаюсь захватить полные GC, которые встречаются в нашем приложении Java. До сих пор у меня есть два подхода, как с недостатками:Программный захват полного количества GC

  1. опрашивать GarbageCollectorMXBean Объекты каждые х секунд, затем потянуть время GC и количество GC с момента последнего опроса и попытаться обнаружить, как долго произошло GC. К сожалению, с этим мы не знаем, является ли это Full GC.
  2. Используйте javax.management.NotificationListener, подписаться на уведомления GarbageCollectorMXBean. Теоретически приложение будет уведомляться, когда происходит GC, с текстом «конец основного GC» и «конец младшего GC», а также причина. Недостатком такого подхода является то, что продолжительность кажется дико неправильной (иногда показывая 898 секунд, когда журнал GC покажет .2 секунды), и есть таинственный случай «Нет GC», который, по-видимому, указывает, что GC не был фактически выполнен (как в журнале gc не было записи).

Гибридный подход возможен, когда GC MXBeans может быть запрошен, когда я получаю уведомление GC, а затем проверьте, был ли GC выполнен. Недостатком этого является то, что причина отсутствия GC может по-прежнему запускаться в конце «основного GC» и вызывать сложность логики.

То, что я действительно получаю, - это общая сумма времени, в течение которого приложение было приостановлено из-за операций GC. Я полагаю, что захват полной GC будет достаточным, чтобы указать, что приложение остановилось в течение определенного периода времени, так что, если мы обнаружим частые GC, мы знаем, что это тяжелое использование или у приложения скоро закончится память. Есть ли лучшее решение для определения времени GC и есть ли полный GC?

Редактировать: Чтобы быть ясным, я хотел бы зафиксировать эту информацию внутри данной JVM. Эта JVM контролируется отдельной командой, и мы не нуждаемся в контроле над аргументами JVM, которые они задают, мы можем давать рекомендации. Идея аналогична полетному самописцу, но вместо этого предоставляет информацию в режиме реального времени администраторам.

ответ

2

Если вы используете горячие точки GC, то вы можете использовать новое/старое различие, а не параллельное/полное. Но этого в большинстве случаев достаточно.

В этом случае я бы опрашивать, и до использования/после информаций в #LastGCInfo от соответствующего поколения:

Например java.lang:type=GarbageCollector,name=PS Scavenge является молодым поколением и java.lang:type=GarbageCollector,name=PS MarkSweep старого поколения, когда ParallelOld включена.

С CMS вы видите одновременно коллекции и полные коллекции в java.lang:type=GarbageCollector,name=ConcurrentMarkSweep (но, надеюсь, никогда нет полной коллекции :)

Это GC и VM конкретно, поэтому она нуждается в некоторых эвристики для поддержки всех платформ, вы заботитесь о ,

Я думаю, что более мелкозернистый мониторинг возможен только с событиями Flight Recorder, но у меня нет опыта с этим.

+0

Оба они обычно возвращаются с java.lang.management.ManagementFactory.getGarbageCollectorMXBeans(), верно? – btz

+0

@btz Я думаю, да. – eckes

+0

Это в конечном итоге приблизило меня к цели, я в конечном итоге ожидал уведомления GC от сборщиков мусора, после того как я получил уведомление о событии GC, я бы проанализировал предыдущие значения общего времени и общее количество GC для определить среднее время с момента последнего уведомления. Это позволит мне получить причину, имя GC и эффективное время GC. – btz

1

Если вы можете получить доступ к файловой системе JVM, о которой идет речь, вы можете сообщить JVM о регистрации информации о сборках мусора.

Для Oracle Java 8 нескольких флагов существуют (от http://docs.oracle.com/javase/8/docs/technotes/tools/unix/java.html#BABFAFAE)


-XX: + PrintGC

Разрешена печать сообщений на каждом СУ. По умолчанию эта опция отключена.

-XX: + PrintGCApplicationConcurrentTime

Разрешена печать, сколько времени прошло с момента последней паузы (например, пауза GC). По умолчанию эта опция отключена.

-XX: + PrintGCApplicationStoppedTime

Позволяет печатать сколько времени паузы (например, GC пауза) продолжалась. По умолчанию эта опция отключена.

-XX: + PrintGCDateStamps

Разрешена печать с отметкой даты на каждом СУ. По умолчанию эта опция отключена.

-XX: + PrintGCDetails

Разрешена печать подробных сообщений на каждом СУ. По умолчанию эта опция отключена.

-XX: + PrintGCTaskTimeStamps

Разрешена печать штампов времени для каждой отдельной задачи потоков GC работника. По умолчанию эта опция отключена.

-XX: + PrintGCTimeStamps

Разрешена печать штампов времени на каждом СУ. По умолчанию эта опция отключена.

+0

Чтобы это было ясно, это решение могло бы либо захватить стандартную версию, либо журнал gc, а затем разобрать эту информацию, чтобы определить, произошел ли полный GC? Не будет ли анализ файла журнала сложным, так как изменение любого из вышеупомянутых флагов изменит формат вывода? – btz

+0

Это зависит от поставщика. Я еще не просмотрел формат журнала Java 8.Журнал Java 7 gc был достаточно легким диалектом XMl. –

+0

Java 7 и 8 logfiles довольно перепутаны. Если вы видели диалект XML, то это были журналы IBM, которые довольно хороши. IBM также имеет буфер трассировки для доступа к этим структурированным событиям. – eckes

0

Вы можете слушать уведомления GC. См. Код ниже.

Я перекрестно передал этот ответ здесь, поскольку он кажется актуальным. Основываясь на pointers given by @the8472 in this question, я создал более полный образец для регистрации GC изнутри JVM (и таким образом обнаружил/подсчитал его). Я надеюсь, что это сэкономит кому-то какое-то время :)

package fi.pelam.gclogutil; 
import java.lang.management.*; 
import java.util.Map; 
import javax.management.openmbean.CompositeData; 
import javax.management.*; 

import com.sun.management.GarbageCollectionNotificationInfo; 
import com.sun.management.GcInfo; 

public class GcLogUtil { 
    static public void startLoggingGc() { 
     // http://www.programcreek.com/java-api-examples/index.php?class=javax.management.MBeanServerConnection&method=addNotificationListener 
     // https://docs.oracle.com/javase/8/docs/jre/api/management/extension/com/sun/management/GarbageCollectionNotificationInfo.html#GARBAGE_COLLECTION_NOTIFICATION 
     for (GarbageCollectorMXBean gcMbean : ManagementFactory.getGarbageCollectorMXBeans()) { 
      try { 
       ManagementFactory.getPlatformMBeanServer(). 
         addNotificationListener(gcMbean.getObjectName(), listener, null,null); 
      } catch (Exception e) { 
       e.printStackTrace(); 
      } 
     } 
    } 

    static private NotificationListener listener = new NotificationListener() { 
     @Override 
     public void handleNotification(Notification notification, Object handback) { 
      if (notification.getType().equals(GarbageCollectionNotificationInfo.GARBAGE_COLLECTION_NOTIFICATION)) { 
       // https://docs.oracle.com/javase/8/docs/jre/api/management/extension/com/sun/management/GarbageCollectionNotificationInfo.html 
       CompositeData cd = (CompositeData) notification.getUserData(); 
       GarbageCollectionNotificationInfo gcNotificationInfo = GarbageCollectionNotificationInfo.from(cd); 
       GcInfo gcInfo = gcNotificationInfo.getGcInfo(); 
       System.out.println("GarbageCollection: "+ 
         gcNotificationInfo.getGcAction() + " " + 
         gcNotificationInfo.getGcName() + 
         " duration: " + gcInfo.getDuration() + "ms" + 
         " used: " + sumUsedMb(gcInfo.getMemoryUsageBeforeGc()) + "MB" + 
         " -> " + sumUsedMb(gcInfo.getMemoryUsageAfterGc()) + "MB"); 
      } 
     } 
    }; 

    static private long sumUsedMb(Map<String, MemoryUsage> memUsages) { 
     long sum = 0; 
     for (MemoryUsage memoryUsage : memUsages.values()) { 
      sum += memoryUsage.getUsed(); 
     } 
     return sum/(1024 * 1024); 
    } 
} 
Смежные вопросы