聊聊springboot1.x及2.x的JvmGcMetrics的区别

本文主要研究一下springboot1.x及2.x的JvmGcMetrics的区别

springboot1.x的jvm gc metrics

springboot1.x没有JvmGcMetrics这个类,它在SystemPublicMetrics中简单采集了jvm的几个指标
spring-boot-actuator-1.5.9.RELEASE-sources.jar!/org/springframework/boot/actuate/endpoint/SystemPublicMetrics.java

    /**
     * Add JVM heap metrics.
     * @param result the result
     */
    protected void addHeapMetrics(Collection<Metric<?>> result) {
        MemoryUsage memoryUsage = ManagementFactory.getMemoryMXBean()
                .getHeapMemoryUsage();
        result.add(newMemoryMetric("heap.committed", memoryUsage.getCommitted()));
        result.add(newMemoryMetric("heap.init", memoryUsage.getInit()));
        result.add(newMemoryMetric("heap.used", memoryUsage.getUsed()));
        result.add(newMemoryMetric("heap", memoryUsage.getMax()));
    }

    /**
     * Add JVM non-heap metrics.
     * @param result the result
     */
    private void addNonHeapMetrics(Collection<Metric<?>> result) {
        MemoryUsage memoryUsage = ManagementFactory.getMemoryMXBean()
                .getNonHeapMemoryUsage();
        result.add(newMemoryMetric("nonheap.committed", memoryUsage.getCommitted()));
        result.add(newMemoryMetric("nonheap.init", memoryUsage.getInit()));
        result.add(newMemoryMetric("nonheap.used", memoryUsage.getUsed()));
        result.add(newMemoryMetric("nonheap", memoryUsage.getMax()));
    }

    /**
     * Add garbage collection metrics.
     * @param result the result
     */
    protected void addGarbageCollectionMetrics(Collection<Metric<?>> result) {
        List<GarbageCollectorMXBean> garbageCollectorMxBeans = ManagementFactory
                .getGarbageCollectorMXBeans();
        for (GarbageCollectorMXBean garbageCollectorMXBean : garbageCollectorMxBeans) {
            String name = beautifyGcName(garbageCollectorMXBean.getName());
            result.add(new Metric<Long>("gc." + name + ".count",
                    garbageCollectorMXBean.getCollectionCount()));
            result.add(new Metric<Long>("gc." + name + ".time",
                    garbageCollectorMXBean.getCollectionTime()));
        }
    }

这个gc的count及time是个累积量

springboot2.x的JvmGcMetrics

springboot2.x改为使用micrometer来进行metrics采集,其中gc相关的在JvmGcMetrics
micrometer-core-1.0.3-sources.jar!/io/micrometer/core/instrument/binder/jvm/JvmGcMetrics.java

/**
 * Record metrics that report a number of statistics related to garbage
 * collection emanating from the MXBean and also adds information about GC causes.
 *
 * @see GarbageCollectorMXBean
 */
@NonNullApi
@NonNullFields
public class JvmGcMetrics implements MeterBinder {

    private static final Logger logger = LoggerFactory.getLogger(JvmGcMetrics.class);

    private boolean managementExtensionsPresent = isManagementExtensionsPresent();

    private Iterable<Tag> tags;

    @Nullable
    private String youngGenPoolName;

    @Nullable
    private String oldGenPoolName;

    public JvmGcMetrics() {
        this(emptyList());
    }

    public JvmGcMetrics(Iterable<Tag> tags) {
        for (MemoryPoolMXBean mbean : ManagementFactory.getMemoryPoolMXBeans()) {
            if (isYoungGenPool(mbean.getName()))
                youngGenPoolName = mbean.getName();
            if (isOldGenPool(mbean.getName()))
                oldGenPoolName = mbean.getName();
        }
        this.tags = tags;
    }

    @Override
    public void bindTo(MeterRegistry registry) {
        AtomicLong maxDataSize = new AtomicLong(0L);
        Gauge.builder("jvm.gc.max.data.size", maxDataSize, AtomicLong::get)
            .tags(tags)
            .description("Max size of old generation memory pool")
            .baseUnit("bytes")
            .register(registry);

        AtomicLong liveDataSize = new AtomicLong(0L);

        Gauge.builder("jvm.gc.live.data.size", liveDataSize, AtomicLong::get)
            .tags(tags)
            .description("Size of old generation memory pool after a full GC")
            .baseUnit("bytes")
            .register(registry);

        Counter promotedBytes = Counter.builder("jvm.gc.memory.promoted").tags(tags)
            .baseUnit("bytes")
            .description("Count of positive increases in the size of the old generation memory pool before GC to after GC")
            .register(registry);

        Counter allocatedBytes = Counter.builder("jvm.gc.memory.allocated").tags(tags)
            .baseUnit("bytes")
            .description("Incremented for an increase in the size of the young generation memory pool after one GC to before the next")
            .register(registry);

        if (this.managementExtensionsPresent) {
            // start watching for GC notifications
            final AtomicLong youngGenSizeAfter = new AtomicLong(0L);

            for (GarbageCollectorMXBean mbean : ManagementFactory.getGarbageCollectorMXBeans()) {
                if (mbean instanceof NotificationEmitter) {
                    ((NotificationEmitter) mbean).addNotificationListener((notification, ref) -> {
                        final String type = notification.getType();
                        if (type.equals(GarbageCollectionNotificationInfo.GARBAGE_COLLECTION_NOTIFICATION)) {
                            CompositeData cd = (CompositeData) notification.getUserData();
                            GarbageCollectionNotificationInfo notificationInfo = GarbageCollectionNotificationInfo.from(cd);

                            if (isConcurrentPhase(notificationInfo.getGcCause())) {
                                Timer.builder("jvm.gc.concurrent.phase.time")
                                        .tags(tags)
                                        .tags("action", notificationInfo.getGcAction(), "cause", notificationInfo.getGcCause())
                                        .description("Time spent in concurrent phase")
                                        .register(registry)
                                        .record(notificationInfo.getGcInfo().getDuration(), TimeUnit.MILLISECONDS);
                            } else {
                                Timer.builder("jvm.gc.pause")
                                        .tags(tags)
                                        .tags("action", notificationInfo.getGcAction(),
                                                "cause", notificationInfo.getGcCause())
                                        .description("Time spent in GC pause")
                                        .register(registry)
                                        .record(notificationInfo.getGcInfo().getDuration(), TimeUnit.MILLISECONDS);
                            }

                            GcInfo gcInfo = notificationInfo.getGcInfo();

                            // Update promotion and allocation counters
                            final Map<String, MemoryUsage> before = gcInfo.getMemoryUsageBeforeGc();
                            final Map<String, MemoryUsage> after = gcInfo.getMemoryUsageAfterGc();

                            if (oldGenPoolName != null) {
                                final long oldBefore = before.get(oldGenPoolName).getUsed();
                                final long oldAfter = after.get(oldGenPoolName).getUsed();
                                final long delta = oldAfter - oldBefore;
                                if (delta > 0L) {
                                    promotedBytes.increment(delta);
                                }

                                // Some GC implementations such as G1 can reduce the old gen size as part of a minor GC. To track the
                                // live data size we record the value if we see a reduction in the old gen heap size or
                                // after a major GC.
                                if (oldAfter < oldBefore || GcGenerationAge.fromName(notificationInfo.getGcName()) == GcGenerationAge.OLD) {
                                    liveDataSize.set(oldAfter);
                                    final long oldMaxAfter = after.get(oldGenPoolName).getMax();
                                    maxDataSize.set(oldMaxAfter);
                                }
                            }

                            if (youngGenPoolName != null) {
                                final long youngBefore = before.get(youngGenPoolName).getUsed();
                                final long youngAfter = after.get(youngGenPoolName).getUsed();
                                final long delta = youngBefore - youngGenSizeAfter.get();
                                youngGenSizeAfter.set(youngAfter);
                                if (delta > 0L) {
                                    allocatedBytes.increment(delta);
                                }
                            }
                        }
                    }, null, null);
                }
            }
        }
    }
//......
}

可以看到gc相关部分改为使用jmx的NotificationEmitter机制来更新数据;gc cause采用了Timer类型来采集。

由于micrometer支持tag,所以这里JvmGcMetrics给gc cause添加了action(比如end of minor GC)及cause(比如G1 Evacuation Pause)两个tag,以支持更细粒度的gc pause指标统计。

小结

springboot1.x的gc time及count是个累积量,而springboot2.x使用micrometer的JvmGcMetrics,其gc的pause指标就变成Timer类型。Timer类型的话,相当于Meter加上Histogram,Meter记录的是瞬时值,而Histogram会对这些瞬时值进行分布统计。除此之外springboot2.x版本还给gc pause打上了action及cause的tag,支持更细粒度的指标统计查询。

doc

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

  • Spring Boot 参考指南 介绍 转载自:https://www.gitbook.com/book/qbgb...
    毛宇鹏阅读 47,204评论 6 342
  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 136,224评论 19 139
  • 系统开发到一定的阶段,线上的机器越来越多,就需要一些监控了,除了服务器的监控,业务方面也需要一些监控服务。Metr...
    whthomas阅读 34,547评论 12 47
  • 逻辑回归(Logistic Regression)是一种用于解决二分类(0 or 1)问题的机器学习方法,用于估计...
    Alfie20阅读 4,944评论 0 1
  • 霍尔顿·考尔菲德到家见了妹妹菲茨之后就打算去投奔安多里尼夫妇,在安多里尼的公寓,塞林格借塞林格之口说出了自己对于青...
    忘南川Lethe阅读 4,400评论 3 1

友情链接更多精彩内容