001package io.prometheus.client.hotspot;
002
003import io.prometheus.client.Collector;
004import io.prometheus.client.GaugeMetricFamily;
005
006import java.lang.management.ManagementFactory;
007import java.lang.management.MemoryMXBean;
008import java.lang.management.MemoryPoolMXBean;
009import java.lang.management.MemoryUsage;
010import java.util.ArrayList;
011import java.util.Collections;
012import java.util.List;
013
014/**
015 * Exports metrics about JVM memory areas.
016 * <p>
017 * Example usage:
018 * <pre>
019 * {@code
020 *   new MemoryPoolsExports().register();
021 * }
022 * </pre>
023 * Example metrics being exported:
024 * <pre>
025 *   jvm_memory_bytes_used{area="heap"} 2000000
026 *   jvm_memory_bytes_committed{area="nonheap"} 200000
027 *   jvm_memory_bytes_max{area="nonheap"} 2000000
028 *   jvm_memory_pool_bytes_used{pool="PS Eden Space"} 2000
029 * </pre>
030 */
031public class MemoryPoolsExports extends Collector {
032  private final MemoryMXBean memoryBean;
033  private final List<MemoryPoolMXBean> poolBeans;
034
035  public MemoryPoolsExports() {
036    this(
037        ManagementFactory.getMemoryMXBean(),
038        ManagementFactory.getMemoryPoolMXBeans());
039  }
040
041  public MemoryPoolsExports(MemoryMXBean memoryBean,
042                             List<MemoryPoolMXBean> poolBeans) {
043    this.memoryBean = memoryBean;
044    this.poolBeans = poolBeans;
045  }
046
047  void addMemoryAreaMetrics(List<MetricFamilySamples> sampleFamilies) {
048    MemoryUsage heapUsage = memoryBean.getHeapMemoryUsage();
049    MemoryUsage nonHeapUsage = memoryBean.getNonHeapMemoryUsage();
050
051    GaugeMetricFamily finalizer = new GaugeMetricFamily(
052        "jvm_memory_objects_pending_finalization",
053        "The number of objects waiting in the finalizer queue.",
054        memoryBean.getObjectPendingFinalizationCount());
055    sampleFamilies.add(finalizer);
056
057    GaugeMetricFamily used = new GaugeMetricFamily(
058        "jvm_memory_bytes_used",
059        "Used bytes of a given JVM memory area.",
060        Collections.singletonList("area"));
061    used.addMetric(Collections.singletonList("heap"), heapUsage.getUsed());
062    used.addMetric(Collections.singletonList("nonheap"), nonHeapUsage.getUsed());
063    sampleFamilies.add(used);
064
065    GaugeMetricFamily committed = new GaugeMetricFamily(
066        "jvm_memory_bytes_committed",
067        "Committed (bytes) of a given JVM memory area.",
068        Collections.singletonList("area"));
069    committed.addMetric(Collections.singletonList("heap"), heapUsage.getCommitted());
070    committed.addMetric(Collections.singletonList("nonheap"), nonHeapUsage.getCommitted());
071    sampleFamilies.add(committed);
072
073    GaugeMetricFamily max = new GaugeMetricFamily(
074        "jvm_memory_bytes_max",
075        "Max (bytes) of a given JVM memory area.",
076        Collections.singletonList("area"));
077    max.addMetric(Collections.singletonList("heap"), heapUsage.getMax());
078    max.addMetric(Collections.singletonList("nonheap"), nonHeapUsage.getMax());
079    sampleFamilies.add(max);
080
081    GaugeMetricFamily init = new GaugeMetricFamily(
082        "jvm_memory_bytes_init",
083        "Initial bytes of a given JVM memory area.",
084        Collections.singletonList("area"));
085    init.addMetric(Collections.singletonList("heap"), heapUsage.getInit());
086    init.addMetric(Collections.singletonList("nonheap"), nonHeapUsage.getInit());
087    sampleFamilies.add(init);
088  }
089
090  void addMemoryPoolMetrics(List<MetricFamilySamples> sampleFamilies) {
091
092    // Note: The Prometheus naming convention is that units belong at the end of the metric name.
093    // For new metrics like jvm_memory_pool_collection_used_bytes we follow that convention.
094    // For old metrics like jvm_memory_pool_bytes_used we keep the names as they are to avoid a breaking change.
095
096    GaugeMetricFamily used = new GaugeMetricFamily(
097        "jvm_memory_pool_bytes_used",
098        "Used bytes of a given JVM memory pool.",
099        Collections.singletonList("pool"));
100    sampleFamilies.add(used);
101    GaugeMetricFamily committed = new GaugeMetricFamily(
102        "jvm_memory_pool_bytes_committed",
103        "Committed bytes of a given JVM memory pool.",
104        Collections.singletonList("pool"));
105    sampleFamilies.add(committed);
106    GaugeMetricFamily max = new GaugeMetricFamily(
107        "jvm_memory_pool_bytes_max",
108        "Max bytes of a given JVM memory pool.",
109        Collections.singletonList("pool"));
110    sampleFamilies.add(max);
111    GaugeMetricFamily init = new GaugeMetricFamily(
112        "jvm_memory_pool_bytes_init",
113        "Initial bytes of a given JVM memory pool.",
114        Collections.singletonList("pool"));
115    sampleFamilies.add(init);
116    GaugeMetricFamily collectionUsed = new GaugeMetricFamily(
117        "jvm_memory_pool_collection_used_bytes",
118        "Used bytes after last collection of a given JVM memory pool.",
119        Collections.singletonList("pool"));
120    sampleFamilies.add(collectionUsed);
121    GaugeMetricFamily collectionCommitted = new GaugeMetricFamily(
122        "jvm_memory_pool_collection_committed_bytes",
123        "Committed after last collection bytes of a given JVM memory pool.",
124        Collections.singletonList("pool"));
125    sampleFamilies.add(collectionCommitted);
126    GaugeMetricFamily collectionMax = new GaugeMetricFamily(
127        "jvm_memory_pool_collection_max_bytes",
128        "Max bytes after last collection of a given JVM memory pool.",
129        Collections.singletonList("pool"));
130    sampleFamilies.add(collectionMax);
131    GaugeMetricFamily collectionInit = new GaugeMetricFamily(
132        "jvm_memory_pool_collection_init_bytes",
133        "Initial after last collection bytes of a given JVM memory pool.",
134        Collections.singletonList("pool"));
135    sampleFamilies.add(collectionInit);
136    for (final MemoryPoolMXBean pool : poolBeans) {
137      MemoryUsage poolUsage = pool.getUsage();
138      used.addMetric(
139          Collections.singletonList(pool.getName()),
140          poolUsage.getUsed());
141      committed.addMetric(
142          Collections.singletonList(pool.getName()),
143          poolUsage.getCommitted());
144      max.addMetric(
145          Collections.singletonList(pool.getName()),
146          poolUsage.getMax());
147      init.addMetric(
148          Collections.singletonList(pool.getName()),
149          poolUsage.getInit());
150      MemoryUsage collectionPoolUsage = pool.getCollectionUsage();
151      if (collectionPoolUsage != null) {
152          collectionUsed.addMetric(
153              Collections.singletonList(pool.getName()),
154              collectionPoolUsage.getUsed());
155          collectionCommitted.addMetric(
156              Collections.singletonList(pool.getName()),
157              collectionPoolUsage.getCommitted());
158          collectionMax.addMetric(
159              Collections.singletonList(pool.getName()),
160              collectionPoolUsage.getMax());
161          collectionInit.addMetric(
162              Collections.singletonList(pool.getName()),
163              collectionPoolUsage.getInit());
164      }
165    }
166  }
167
168  public List<MetricFamilySamples> collect() {
169    List<MetricFamilySamples> mfs = new ArrayList<MetricFamilySamples>();
170    addMemoryAreaMetrics(mfs);
171    addMemoryPoolMetrics(mfs);
172    return mfs;
173  }
174}