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