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