001package io.prometheus.metrics.instrumentation.jvm;
002
003import io.prometheus.metrics.config.PrometheusProperties;
004import io.prometheus.metrics.core.metrics.GaugeWithCallback;
005import io.prometheus.metrics.model.registry.PrometheusRegistry;
006import io.prometheus.metrics.model.snapshots.Unit;
007import java.lang.management.ManagementFactory;
008import java.lang.management.MemoryMXBean;
009import java.lang.management.MemoryPoolMXBean;
010import java.lang.management.MemoryUsage;
011import java.util.List;
012import java.util.function.Consumer;
013import java.util.function.Function;
014
015/**
016 * JVM memory metrics. The {@link JvmMemoryMetrics} are registered as part of the {@link JvmMetrics}
017 * like this:
018 *
019 * <pre>{@code
020 * JvmMetrics.builder().register();
021 * }</pre>
022 *
023 * However, if you want only the {@link JvmMemoryMetrics} you can also register them directly:
024 *
025 * <pre>{@code
026 * JvmMemoryMetrics.builder().register();
027 * }</pre>
028 *
029 * Example metrics being exported:
030 *
031 * <pre>
032 * # HELP jvm_memory_committed_bytes Committed (bytes) of a given JVM memory area.
033 * # TYPE jvm_memory_committed_bytes gauge
034 * jvm_memory_committed_bytes{area="heap"} 4.98597888E8
035 * jvm_memory_committed_bytes{area="nonheap"} 1.1993088E7
036 * # HELP jvm_memory_init_bytes Initial bytes of a given JVM memory area.
037 * # TYPE jvm_memory_init_bytes gauge
038 * jvm_memory_init_bytes{area="heap"} 5.20093696E8
039 * jvm_memory_init_bytes{area="nonheap"} 2555904.0
040 * # HELP jvm_memory_max_bytes Max (bytes) of a given JVM memory area.
041 * # TYPE jvm_memory_max_bytes gauge
042 * jvm_memory_max_bytes{area="heap"} 7.38983936E9
043 * jvm_memory_max_bytes{area="nonheap"} -1.0
044 * # HELP jvm_memory_objects_pending_finalization The number of objects waiting in the finalizer queue.
045 * # TYPE jvm_memory_objects_pending_finalization gauge
046 * jvm_memory_objects_pending_finalization 0.0
047 * # HELP jvm_memory_pool_collection_committed_bytes Committed after last collection bytes of a given JVM memory pool.
048 * # TYPE jvm_memory_pool_collection_committed_bytes gauge
049 * jvm_memory_pool_collection_committed_bytes{pool="PS Eden Space"} 1.30023424E8
050 * jvm_memory_pool_collection_committed_bytes{pool="PS Old Gen"} 3.47078656E8
051 * jvm_memory_pool_collection_committed_bytes{pool="PS Survivor Space"} 2.1495808E7
052 * # HELP jvm_memory_pool_collection_init_bytes Initial after last collection bytes of a given JVM memory pool.
053 * # TYPE jvm_memory_pool_collection_init_bytes gauge
054 * jvm_memory_pool_collection_init_bytes{pool="PS Eden Space"} 1.30023424E8
055 * jvm_memory_pool_collection_init_bytes{pool="PS Old Gen"} 3.47078656E8
056 * jvm_memory_pool_collection_init_bytes{pool="PS Survivor Space"} 2.1495808E7
057 * # HELP jvm_memory_pool_collection_max_bytes Max bytes after last collection of a given JVM memory pool.
058 * # TYPE jvm_memory_pool_collection_max_bytes gauge
059 * jvm_memory_pool_collection_max_bytes{pool="PS Eden Space"} 2.727870464E9
060 * jvm_memory_pool_collection_max_bytes{pool="PS Old Gen"} 5.542248448E9
061 * jvm_memory_pool_collection_max_bytes{pool="PS Survivor Space"} 2.1495808E7
062 * # HELP jvm_memory_pool_collection_used_bytes Used bytes after last collection of a given JVM memory pool.
063 * # TYPE jvm_memory_pool_collection_used_bytes gauge
064 * jvm_memory_pool_collection_used_bytes{pool="PS Eden Space"} 0.0
065 * jvm_memory_pool_collection_used_bytes{pool="PS Old Gen"} 1249696.0
066 * jvm_memory_pool_collection_used_bytes{pool="PS Survivor Space"} 0.0
067 * # HELP jvm_memory_pool_committed_bytes Committed bytes of a given JVM memory pool.
068 * # TYPE jvm_memory_pool_committed_bytes gauge
069 * jvm_memory_pool_committed_bytes{pool="Code Cache"} 4128768.0
070 * jvm_memory_pool_committed_bytes{pool="Compressed Class Space"} 917504.0
071 * jvm_memory_pool_committed_bytes{pool="Metaspace"} 6946816.0
072 * jvm_memory_pool_committed_bytes{pool="PS Eden Space"} 1.30023424E8
073 * jvm_memory_pool_committed_bytes{pool="PS Old Gen"} 3.47078656E8
074 * jvm_memory_pool_committed_bytes{pool="PS Survivor Space"} 2.1495808E7
075 * # HELP jvm_memory_pool_init_bytes Initial bytes of a given JVM memory pool.
076 * # TYPE jvm_memory_pool_init_bytes gauge
077 * jvm_memory_pool_init_bytes{pool="Code Cache"} 2555904.0
078 * jvm_memory_pool_init_bytes{pool="Compressed Class Space"} 0.0
079 * jvm_memory_pool_init_bytes{pool="Metaspace"} 0.0
080 * jvm_memory_pool_init_bytes{pool="PS Eden Space"} 1.30023424E8
081 * jvm_memory_pool_init_bytes{pool="PS Old Gen"} 3.47078656E8
082 * jvm_memory_pool_init_bytes{pool="PS Survivor Space"} 2.1495808E7
083 * # HELP jvm_memory_pool_max_bytes Max bytes of a given JVM memory pool.
084 * # TYPE jvm_memory_pool_max_bytes gauge
085 * jvm_memory_pool_max_bytes{pool="Code Cache"} 2.5165824E8
086 * jvm_memory_pool_max_bytes{pool="Compressed Class Space"} 1.073741824E9
087 * jvm_memory_pool_max_bytes{pool="Metaspace"} -1.0
088 * jvm_memory_pool_max_bytes{pool="PS Eden Space"} 2.727870464E9
089 * jvm_memory_pool_max_bytes{pool="PS Old Gen"} 5.542248448E9
090 * jvm_memory_pool_max_bytes{pool="PS Survivor Space"} 2.1495808E7
091 * # HELP jvm_memory_pool_used_bytes Used bytes of a given JVM memory pool.
092 * # TYPE jvm_memory_pool_used_bytes gauge
093 * jvm_memory_pool_used_bytes{pool="Code Cache"} 4065472.0
094 * jvm_memory_pool_used_bytes{pool="Compressed Class Space"} 766680.0
095 * jvm_memory_pool_used_bytes{pool="Metaspace"} 6659432.0
096 * jvm_memory_pool_used_bytes{pool="PS Eden Space"} 7801536.0
097 * jvm_memory_pool_used_bytes{pool="PS Old Gen"} 1249696.0
098 * jvm_memory_pool_used_bytes{pool="PS Survivor Space"} 0.0
099 * # HELP jvm_memory_used_bytes Used bytes of a given JVM memory area.
100 * # TYPE jvm_memory_used_bytes gauge
101 * jvm_memory_used_bytes{area="heap"} 9051232.0
102 * jvm_memory_used_bytes{area="nonheap"} 1.1490688E7
103 * </pre>
104 */
105public class JvmMemoryMetrics {
106
107  private static final String JVM_MEMORY_OBJECTS_PENDING_FINALIZATION =
108      "jvm_memory_objects_pending_finalization";
109  private static final String JVM_MEMORY_USED_BYTES = "jvm_memory_used_bytes";
110  private static final String JVM_MEMORY_COMMITTED_BYTES = "jvm_memory_committed_bytes";
111  private static final String JVM_MEMORY_MAX_BYTES = "jvm_memory_max_bytes";
112  private static final String JVM_MEMORY_INIT_BYTES = "jvm_memory_init_bytes";
113  private static final String JVM_MEMORY_POOL_USED_BYTES = "jvm_memory_pool_used_bytes";
114  private static final String JVM_MEMORY_POOL_COMMITTED_BYTES = "jvm_memory_pool_committed_bytes";
115  private static final String JVM_MEMORY_POOL_MAX_BYTES = "jvm_memory_pool_max_bytes";
116  private static final String JVM_MEMORY_POOL_INIT_BYTES = "jvm_memory_pool_init_bytes";
117  private static final String JVM_MEMORY_POOL_COLLECTION_USED_BYTES =
118      "jvm_memory_pool_collection_used_bytes";
119  private static final String JVM_MEMORY_POOL_COLLECTION_COMMITTED_BYTES =
120      "jvm_memory_pool_collection_committed_bytes";
121  private static final String JVM_MEMORY_POOL_COLLECTION_MAX_BYTES =
122      "jvm_memory_pool_collection_max_bytes";
123  private static final String JVM_MEMORY_POOL_COLLECTION_INIT_BYTES =
124      "jvm_memory_pool_collection_init_bytes";
125
126  private final PrometheusProperties config;
127  private final MemoryMXBean memoryBean;
128  private final List<MemoryPoolMXBean> poolBeans;
129
130  private JvmMemoryMetrics(
131      List<MemoryPoolMXBean> poolBeans, MemoryMXBean memoryBean, PrometheusProperties config) {
132    this.config = config;
133    this.poolBeans = poolBeans;
134    this.memoryBean = memoryBean;
135  }
136
137  private void register(PrometheusRegistry registry) {
138
139    GaugeWithCallback.builder(config)
140        .name(JVM_MEMORY_OBJECTS_PENDING_FINALIZATION)
141        .help("The number of objects waiting in the finalizer queue.")
142        .callback(callback -> callback.call(memoryBean.getObjectPendingFinalizationCount()))
143        .register(registry);
144
145    GaugeWithCallback.builder(config)
146        .name(JVM_MEMORY_USED_BYTES)
147        .help("Used bytes of a given JVM memory area.")
148        .unit(Unit.BYTES)
149        .labelNames("area")
150        .callback(
151            callback -> {
152              callback.call(memoryBean.getHeapMemoryUsage().getUsed(), "heap");
153              callback.call(memoryBean.getNonHeapMemoryUsage().getUsed(), "nonheap");
154            })
155        .register(registry);
156
157    GaugeWithCallback.builder(config)
158        .name(JVM_MEMORY_COMMITTED_BYTES)
159        .help("Committed (bytes) of a given JVM memory area.")
160        .unit(Unit.BYTES)
161        .labelNames("area")
162        .callback(
163            callback -> {
164              callback.call(memoryBean.getHeapMemoryUsage().getCommitted(), "heap");
165              callback.call(memoryBean.getNonHeapMemoryUsage().getCommitted(), "nonheap");
166            })
167        .register(registry);
168
169    GaugeWithCallback.builder(config)
170        .name(JVM_MEMORY_MAX_BYTES)
171        .help("Max (bytes) of a given JVM memory area.")
172        .unit(Unit.BYTES)
173        .labelNames("area")
174        .callback(
175            callback -> {
176              callback.call(memoryBean.getHeapMemoryUsage().getMax(), "heap");
177              callback.call(memoryBean.getNonHeapMemoryUsage().getMax(), "nonheap");
178            })
179        .register(registry);
180
181    GaugeWithCallback.builder(config)
182        .name(JVM_MEMORY_INIT_BYTES)
183        .help("Initial bytes of a given JVM memory area.")
184        .unit(Unit.BYTES)
185        .labelNames("area")
186        .callback(
187            callback -> {
188              callback.call(memoryBean.getHeapMemoryUsage().getInit(), "heap");
189              callback.call(memoryBean.getNonHeapMemoryUsage().getInit(), "nonheap");
190            })
191        .register(registry);
192
193    GaugeWithCallback.builder(config)
194        .name(JVM_MEMORY_POOL_USED_BYTES)
195        .help("Used bytes of a given JVM memory pool.")
196        .unit(Unit.BYTES)
197        .labelNames("pool")
198        .callback(makeCallback(poolBeans, MemoryPoolMXBean::getUsage, MemoryUsage::getUsed))
199        .register(registry);
200
201    GaugeWithCallback.builder(config)
202        .name(JVM_MEMORY_POOL_COMMITTED_BYTES)
203        .help("Committed bytes of a given JVM memory pool.")
204        .unit(Unit.BYTES)
205        .labelNames("pool")
206        .callback(makeCallback(poolBeans, MemoryPoolMXBean::getUsage, MemoryUsage::getCommitted))
207        .register(registry);
208
209    GaugeWithCallback.builder(config)
210        .name(JVM_MEMORY_POOL_MAX_BYTES)
211        .help("Max bytes of a given JVM memory pool.")
212        .unit(Unit.BYTES)
213        .labelNames("pool")
214        .callback(makeCallback(poolBeans, MemoryPoolMXBean::getUsage, MemoryUsage::getMax))
215        .register(registry);
216
217    GaugeWithCallback.builder(config)
218        .name(JVM_MEMORY_POOL_INIT_BYTES)
219        .help("Initial bytes of a given JVM memory pool.")
220        .unit(Unit.BYTES)
221        .labelNames("pool")
222        .callback(makeCallback(poolBeans, MemoryPoolMXBean::getUsage, MemoryUsage::getInit))
223        .register(registry);
224
225    GaugeWithCallback.builder(config)
226        .name(JVM_MEMORY_POOL_COLLECTION_USED_BYTES)
227        .help("Used bytes after last collection of a given JVM memory pool.")
228        .unit(Unit.BYTES)
229        .labelNames("pool")
230        .callback(
231            makeCallback(poolBeans, MemoryPoolMXBean::getCollectionUsage, MemoryUsage::getUsed))
232        .register(registry);
233
234    GaugeWithCallback.builder(config)
235        .name(JVM_MEMORY_POOL_COLLECTION_COMMITTED_BYTES)
236        .help("Committed after last collection bytes of a given JVM memory pool.")
237        .unit(Unit.BYTES)
238        .labelNames("pool")
239        .callback(
240            makeCallback(
241                poolBeans, MemoryPoolMXBean::getCollectionUsage, MemoryUsage::getCommitted))
242        .register(registry);
243
244    GaugeWithCallback.builder(config)
245        .name(JVM_MEMORY_POOL_COLLECTION_MAX_BYTES)
246        .help("Max bytes after last collection of a given JVM memory pool.")
247        .unit(Unit.BYTES)
248        .labelNames("pool")
249        .callback(
250            makeCallback(poolBeans, MemoryPoolMXBean::getCollectionUsage, MemoryUsage::getMax))
251        .register(registry);
252
253    GaugeWithCallback.builder(config)
254        .name(JVM_MEMORY_POOL_COLLECTION_INIT_BYTES)
255        .help("Initial after last collection bytes of a given JVM memory pool.")
256        .unit(Unit.BYTES)
257        .labelNames("pool")
258        .callback(
259            makeCallback(poolBeans, MemoryPoolMXBean::getCollectionUsage, MemoryUsage::getInit))
260        .register(registry);
261  }
262
263  private Consumer<GaugeWithCallback.Callback> makeCallback(
264      List<MemoryPoolMXBean> poolBeans,
265      Function<MemoryPoolMXBean, MemoryUsage> memoryUsageFunc,
266      Function<MemoryUsage, Long> valueFunc) {
267    return callback -> {
268      for (MemoryPoolMXBean pool : poolBeans) {
269        MemoryUsage poolUsage = memoryUsageFunc.apply(pool);
270        if (poolUsage != null) {
271          callback.call(valueFunc.apply(poolUsage), pool.getName());
272        }
273      }
274    };
275  }
276
277  public static Builder builder() {
278    return new Builder(PrometheusProperties.get());
279  }
280
281  public static Builder builder(PrometheusProperties config) {
282    return new Builder(config);
283  }
284
285  public static class Builder {
286
287    private final PrometheusProperties config;
288    private MemoryMXBean memoryBean;
289    private List<MemoryPoolMXBean> poolBeans;
290
291    private Builder(PrometheusProperties config) {
292      this.config = config;
293    }
294
295    /** Package private. For testing only. */
296    Builder withMemoryBean(MemoryMXBean memoryBean) {
297      this.memoryBean = memoryBean;
298      return this;
299    }
300
301    /** Package private. For testing only. */
302    Builder withMemoryPoolBeans(List<MemoryPoolMXBean> memoryPoolBeans) {
303      this.poolBeans = memoryPoolBeans;
304      return this;
305    }
306
307    public void register() {
308      register(PrometheusRegistry.defaultRegistry);
309    }
310
311    public void register(PrometheusRegistry registry) {
312      MemoryMXBean bean =
313          this.memoryBean != null ? this.memoryBean : ManagementFactory.getMemoryMXBean();
314      List<MemoryPoolMXBean> poolBeans =
315          this.poolBeans != null ? this.poolBeans : ManagementFactory.getMemoryPoolMXBeans();
316      new JvmMemoryMetrics(poolBeans, bean, config).register(registry);
317    }
318  }
319}