Java 以编程方式捕获完整GC计数

Java 以编程方式捕获完整GC计数,java,garbage-collection,jvm,Java,Garbage Collection,Jvm,我试图捕获Java应用程序中出现的完整GCs。到目前为止,我有两种方法,都有缺陷: 每x秒轮询一次GarbageCollectorMXBean对象,然后尝试提取自上次轮询以来的GC时间和GC计数,并尝试检测GC发生的时间。不幸的是,我们不知道这是否是一个完整的GC 使用javax.management.NotificationListener,订阅垃圾收集器MXBean通知。理论上,当GC发生时,应用程序将收到通知,其中包含文本“主要GC结束”和“次要GC结束”以及原因。这种方法的缺陷在于持续时

我试图捕获Java应用程序中出现的完整GCs。到目前为止,我有两种方法,都有缺陷:

  • 每x秒轮询一次GarbageCollectorMXBean对象,然后尝试提取自上次轮询以来的GC时间和GC计数,并尝试检测GC发生的时间。不幸的是,我们不知道这是否是一个完整的GC
  • 使用javax.management.NotificationListener,订阅垃圾收集器MXBean通知。理论上,当GC发生时,应用程序将收到通知,其中包含文本“主要GC结束”和“次要GC结束”以及原因。这种方法的缺陷在于持续时间似乎非常不正确(有时会显示898秒,而GC日志将显示0.2秒),并且有一个神秘的案例“No GC”,它似乎表明没有实际执行GC(因为GC日志中没有条目)
  • 混合方法是可能的,当我收到GC通知时,可以查询GC MXBeans,然后检查是否执行了GC。这样做的缺点是,No-GC原因可能仍然会在“major-GC”结束时触发,并导致逻辑更加复杂

    我真正想要的是应用程序由于GC操作而暂停的总时间。我认为捕获一个完整的GC就足以表明应用程序已经停止了一段时间,因此,如果我们检测到频繁的GC,我们就知道使用量很大,或者应用程序很快就会耗尽内存。是否有更好的解决方案来检测GC时间以及是否有完整的GC


    编辑:为了清楚起见,我想在给定的JVM中捕获这些信息。这个JVM由一个单独的团队控制,我们不需要控制他们将设置的JVM参数,我们只能提出建议。这个想法类似于flight recorder,但它可以实时向管理员提供信息。

    如果您使用热点特定的GC bean,那么您可以区分新/旧,而不是并发/完整。但在大多数情况下,这已经足够了

    在这种情况下,我将进行投票,并使用相关代的
    #LastGCInfo
    中的before/after信息:

    例如,
    java.lang:type=GarbageCollector,name=PS cleave
    是年轻一代,而启用ParallelOld时,
    java.lang:type=GarbageCollector,name=PS MarkSweep
    是老一代

    使用CMS,您可以在
    java.lang:type=GarbageCollector,name=ConcurrentMarkSweep
    中看到并发集合和完整集合(但希望永远不会有完整集合:)

    这是GC和VM特有的,因此需要一些启发式方法来支持您关心的所有平台


    我认为只有飞行记录器事件才有可能进行更细粒度的监控,但我没有这方面的经验。

    如果您可以访问相关JVM的文件系统,您可以告诉JVM记录有关垃圾收集的信息

    对于Oracle Java 8,存在多个标志(从)


    -XX:+PrintGC

    允许在每个GC打印消息。默认情况下,此选项处于禁用状态

    -XX:+PrintGCApplicationncCurrentTime

    允许打印自上次暂停(例如GC暂停)以来经过的时间。默认情况下,此选项处于禁用状态

    -XX:+PrintGCApplicationStoppedTime

    允许打印暂停(例如GC暂停)持续的时间。默认情况下,此选项处于禁用状态

    -XX:+printgc邮票

    允许在每次GC打印日期戳。默认情况下,此选项处于禁用状态

    -XX:+PrintGCDetails

    允许在每个GC打印详细消息。默认情况下,此选项处于禁用状态

    -XX:+PrintGCTaskTimeStamps

    支持打印每个GC工作线程任务的时间戳。默认情况下,此选项处于禁用状态

    -XX:+printgc时间戳


    允许在每个GC打印时间戳。默认情况下,此选项处于禁用状态。

    您可以侦听GC通知。请参阅下面的代码

    我在这里交叉张贴这个答案,因为它似乎是相关的。基于此,我制作了一个更完整的示例,用于从JVM内部记录GC(从而检测/计算GC)。我希望这能节省一些时间:)

    包fi.pelam.gclogutil;
    导入java.lang.management.*;
    导入java.util.Map;
    导入javax.management.openmbean.CompositeData;
    导入javax.management.*;
    导入com.sun.management.GarbageCollectionNotificationInfo;
    导入com.sun.management.GcInfo;
    公共类GcLogUtil{
    静态公共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
    对于(GarbageCollectorMXBean gcMbean:ManagementFactory.getGarbageCollectorMXBeans()){
    试一试{
    ManagementFactory.getPlatformMBeanServer()。
    addNotificationListener(gcMbean.getObjectName(),listener,null,null);
    }捕获(例外e){
    e、 printStackTrace();
    }
    }
    }
    静态私有NotificationListener=新建NotificationListener(){
    @凌驾
    公共作废处理通知(通知、对象处理){
    if(notification.getType().equals(GarbageCollectionNotificationInfo.GARBAGE\u COLLECTION\u notification)){
    // https://docs.oracle.com/javase/8/docs/jre/api/management/extension/com/sun/management/GarbageCollectionNotificationInfo.html
    CompositeData cd=(CompositeData)通知。getUserData();
    
    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);
        }
    }
    
    GarbageCollectorMXBean fullGcMXBean = null;
            List<GarbageCollectorMXBean> gcMxBeanList = ManagementFactory.getGarbageCollectorMXBeans();
            for(GarbageCollectorMXBean gcMxBean: gcMxBeanList){
                if(gcMxBean.getName().endsWith("MarkSweep")){
                    fullGcMXBean = gcMxBean;
                    break;
                }
            }