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