本文主要研究一下springboot1.x及2.x的JvmGcMetrics的區別java
springboot1.x沒有JvmGcMetrics這個類,它在SystemPublicMetrics中簡單採集了jvm的幾個指標
spring-boot-actuator-1.5.9.RELEASE-sources.jar!/org/springframework/boot/actuate/endpoint/SystemPublicMetrics.javaspring
/** * 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改成使用micrometer來進行metrics採集,其中gc相關的在JvmGcMetrics
micrometer-core-1.0.3-sources.jar!/io/micrometer/core/instrument/binder/jvm/JvmGcMetrics.javasegmentfault
/** * 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指標統計。springboot
springboot1.x的gc time及count是個累積量,而springboot2.x使用micrometer的JvmGcMetrics,其gc的pause指標就變成Timer類型。Timer類型的話,至關於Meter加上Histogram,Meter記錄的是瞬時值,而Histogram會對這些瞬時值進行分佈統計。除此以外springboot2.x版本還給gc pause打上了action及cause的tag,支持更細粒度的指標統計查詢。jvm