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}