summaryrefslogtreecommitdiffstats
path: root/container-disc/src/main/java/com/yahoo/container/jdisc/metric/GarbageCollectionMetrics.java
diff options
context:
space:
mode:
Diffstat (limited to 'container-disc/src/main/java/com/yahoo/container/jdisc/metric/GarbageCollectionMetrics.java')
-rw-r--r--container-disc/src/main/java/com/yahoo/container/jdisc/metric/GarbageCollectionMetrics.java94
1 files changed, 94 insertions, 0 deletions
diff --git a/container-disc/src/main/java/com/yahoo/container/jdisc/metric/GarbageCollectionMetrics.java b/container-disc/src/main/java/com/yahoo/container/jdisc/metric/GarbageCollectionMetrics.java
new file mode 100644
index 00000000000..04fd8572ad4
--- /dev/null
+++ b/container-disc/src/main/java/com/yahoo/container/jdisc/metric/GarbageCollectionMetrics.java
@@ -0,0 +1,94 @@
+package com.yahoo.container.jdisc.metric;
+
+import com.yahoo.jdisc.Metric;
+
+import java.lang.management.GarbageCollectorMXBean;
+import java.lang.management.ManagementFactory;
+import java.time.Clock;
+import java.time.Duration;
+import java.time.Instant;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.Map;
+
+/**
+ * @author ollivir
+ */
+public class GarbageCollectionMetrics {
+ private static final String GC_COUNT = "jdisc.gc.count";
+ private static final String GC_TIME = "jdisc.gc.ms";
+ private static final String DIMENSION_KEY = "gcName";
+
+ public static final Duration REPORTING_INTERVAL = Duration.ofSeconds(62);
+
+ static class GcStats {
+ private final Instant when;
+ private final long count;
+ private final Duration totalRuntime;
+
+ private GcStats(Instant when, long count, Duration totalRuntime) {
+ this.when = when;
+ this.count = count;
+ this.totalRuntime = totalRuntime;
+ }
+ }
+
+ private Map<String, LinkedList<GcStats>> gcStatistics;
+
+ private final Clock clock;
+
+ public GarbageCollectionMetrics(Clock clock) {
+ this.clock = clock;
+ this.gcStatistics = new HashMap<>();
+ collectGcStatistics(clock.instant());
+ }
+
+ private void collectGcStatistics(Instant now) {
+ for (GarbageCollectorMXBean gcBean : ManagementFactory.getGarbageCollectorMXBeans()) {
+ String gcName = gcBean.getName().replace(" ", "");
+ GcStats stats = new GcStats(now, gcBean.getCollectionCount(), Duration.ofMillis(gcBean.getCollectionTime()));
+
+ LinkedList<GcStats> window = gcStatistics.computeIfAbsent(gcName, anyName -> new LinkedList<>());
+ window.addLast(stats);
+ }
+ }
+
+ private void cleanStatistics(Instant now) {
+ Instant oldestToKeep = now.minus(REPORTING_INTERVAL);
+
+ for(Iterator<Map.Entry<String, LinkedList<GcStats>>> it = gcStatistics.entrySet().iterator(); it.hasNext(); ) {
+ Map.Entry<String, LinkedList<GcStats>> entry = it.next();
+ LinkedList<GcStats> history = entry.getValue();
+ while(history.isEmpty() == false && oldestToKeep.isAfter(history.getFirst().when)) {
+ history.removeFirst();
+ }
+ if(history.isEmpty()) {
+ it.remove();
+ }
+ }
+ }
+
+ public void emitMetrics(Metric metric) {
+ Instant now = clock.instant();
+
+ collectGcStatistics(now);
+ cleanStatistics(now);
+
+ for (Map.Entry<String, LinkedList<GcStats>> item : gcStatistics.entrySet()) {
+ GcStats reference = item.getValue().getFirst();
+ GcStats latest = item.getValue().getLast();
+ Map<String, String> contextData = new HashMap<>();
+ contextData.put(DIMENSION_KEY, item.getKey());
+ Metric.Context gcContext = metric.createContext(contextData);
+
+ metric.set(GC_COUNT, latest.count - reference.count, gcContext);
+ metric.set(GC_TIME, latest.totalRuntime.minus(reference.totalRuntime).toMillis(), gcContext);
+ }
+ }
+
+ // partial exposure for testing
+ Map<String, LinkedList<GcStats>> getGcStatistics() {
+ return gcStatistics;
+ }
+}