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