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}