All Downloads are FREE. Search and download functionalities are using the official Maven repository.

io.micrometer.core.instrument.binder.jvm.JvmHeapPressureMetrics Maven / Gradle / Ivy

There is a newer version: 1.13.0
Show newest version
/**
 * Copyright 2020 VMware, Inc.
 * 

* Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at *

* https://www.apache.org/licenses/LICENSE-2.0 *

* Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package io.micrometer.core.instrument.binder.jvm; import com.sun.management.GarbageCollectionNotificationInfo; import com.sun.management.GcInfo; import io.micrometer.core.instrument.Gauge; import io.micrometer.core.instrument.MeterRegistry; import io.micrometer.core.instrument.Tag; import io.micrometer.core.instrument.binder.BaseUnits; import io.micrometer.core.instrument.binder.MeterBinder; import io.micrometer.core.instrument.distribution.TimeWindowSum; import io.micrometer.core.lang.NonNull; import javax.management.ListenerNotFoundException; import javax.management.NotificationEmitter; import javax.management.NotificationListener; import javax.management.openmbean.CompositeData; import java.lang.management.*; import java.time.Duration; import java.util.List; import java.util.Map; import java.util.Set; import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.atomic.AtomicReference; import java.util.stream.Collectors; import static java.util.Collections.emptyList; /** * Provides methods to access measurements of low pool memory and heavy GC overhead as described in * TeamCity's Memory Monitor. * * @author Jon Schneider * @since 1.4.0 */ public class JvmHeapPressureMetrics implements MeterBinder, AutoCloseable { private final Iterable tags; private final List notificationListenerCleanUpRunnables = new CopyOnWriteArrayList<>(); private final long startOfMonitoring = System.nanoTime(); private final Duration lookback; private final TimeWindowSum gcPauseSum; private final AtomicReference lastLongLivedPoolUsageAfterGc = new AtomicReference<>(0.0); private final Set longLivedPoolNames; public JvmHeapPressureMetrics() { this(emptyList(), Duration.ofMinutes(5), Duration.ofMinutes(1)); } public JvmHeapPressureMetrics(Iterable tags, Duration lookback, Duration testEvery) { this.tags = tags; this.lookback = lookback; this.gcPauseSum = new TimeWindowSum((int) lookback.dividedBy(testEvery.toMillis()).toMillis(), testEvery); longLivedPoolNames = JvmMemory.getLongLivedHeapPools().map(MemoryPoolMXBean::getName).collect(Collectors.toSet()); monitor(); } @Override public void bindTo(@NonNull MeterRegistry registry) { if (!longLivedPoolNames.isEmpty()) { Gauge.builder("jvm.memory.usage.after.gc", lastLongLivedPoolUsageAfterGc, AtomicReference::get) .tags(tags) .tag("area", "heap") .tag("pool", "long-lived") .description("The percentage of long-lived heap pool used after the last GC event, in the range [0..1]") .baseUnit(BaseUnits.PERCENT) .register(registry); } Gauge.builder("jvm.gc.overhead", gcPauseSum, pauseSum -> { double overIntervalMillis = Math.min(System.nanoTime() - startOfMonitoring, lookback.toNanos()) / 1e6; return gcPauseSum.poll() / overIntervalMillis; }) .tags(tags) .description("An approximation of the percent of CPU time used by GC activities over the last lookback period or since monitoring began, whichever is shorter, in the range [0..1]") .baseUnit(BaseUnits.PERCENT) .register(registry); } private void monitor() { for (GarbageCollectorMXBean mbean : ManagementFactory.getGarbageCollectorMXBeans()) { if (!(mbean instanceof NotificationEmitter)) { continue; } NotificationListener notificationListener = (notification, ref) -> { CompositeData cd = (CompositeData) notification.getUserData(); GarbageCollectionNotificationInfo notificationInfo = GarbageCollectionNotificationInfo.from(cd); String gcCause = notificationInfo.getGcCause(); GcInfo gcInfo = notificationInfo.getGcInfo(); long duration = gcInfo.getDuration(); if (!JvmMemory.isConcurrentPhase(gcCause, notificationInfo.getGcName())) { gcPauseSum.record(duration); } Map after = gcInfo.getMemoryUsageAfterGc(); if (!longLivedPoolNames.isEmpty()) { final long usedAfter = longLivedPoolNames.stream().mapToLong(pool -> after.get(pool).getUsed()).sum(); double maxAfter = longLivedPoolNames.stream().mapToLong(pool -> after.get(pool).getMax()).sum(); lastLongLivedPoolUsageAfterGc.set(usedAfter / maxAfter); } }; NotificationEmitter notificationEmitter = (NotificationEmitter) mbean; notificationEmitter.addNotificationListener(notificationListener, notification -> notification.getType().equals(GarbageCollectionNotificationInfo.GARBAGE_COLLECTION_NOTIFICATION), null); notificationListenerCleanUpRunnables.add(() -> { try { notificationEmitter.removeNotificationListener(notificationListener); } catch (ListenerNotFoundException ignore) { } }); } } @Override public void close() { notificationListenerCleanUpRunnables.forEach(Runnable::run); } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy