001package io.prometheus.client.hotspot; 002 003import io.prometheus.client.Collector; 004import io.prometheus.client.GaugeMetricFamily; 005import io.prometheus.client.Predicate; 006 007import java.lang.management.ManagementFactory; 008import java.lang.management.MemoryMXBean; 009import java.lang.management.MemoryPoolMXBean; 010import java.lang.management.MemoryUsage; 011import java.util.ArrayList; 012import java.util.Collections; 013import java.util.List; 014 015import static io.prometheus.client.SampleNameFilter.ALLOW_ALL; 016 017/** 018 * Exports metrics about JVM memory areas. 019 * <p> 020 * Example usage: 021 * <pre> 022 * {@code 023 * new MemoryPoolsExports().register(); 024 * } 025 * </pre> 026 * Example metrics being exported: 027 * <pre> 028 * jvm_memory_bytes_used{area="heap"} 2000000 029 * jvm_memory_bytes_committed{area="nonheap"} 200000 030 * jvm_memory_bytes_max{area="nonheap"} 2000000 031 * jvm_memory_pool_bytes_used{pool="PS Eden Space"} 2000 032 * </pre> 033 */ 034public class MemoryPoolsExports extends Collector { 035 036 private static final String JVM_MEMORY_OBJECTS_PENDING_FINALIZATION = "jvm_memory_objects_pending_finalization"; 037 private static final String JVM_MEMORY_BYTES_USED = "jvm_memory_bytes_used"; 038 private static final String JVM_MEMORY_BYTES_COMMITTED = "jvm_memory_bytes_committed"; 039 private static final String JVM_MEMORY_BYTES_MAX = "jvm_memory_bytes_max"; 040 private static final String JVM_MEMORY_BYTES_INIT = "jvm_memory_bytes_init"; 041 042 // Note: The Prometheus naming convention is that units belong at the end of the metric name. 043 // For new metrics like jvm_memory_pool_collection_used_bytes we follow that convention. 044 // For old metrics like jvm_memory_pool_bytes_used we keep the names as they are to avoid a breaking change. 045 046 private static final String JVM_MEMORY_POOL_BYTES_USED = "jvm_memory_pool_bytes_used"; 047 private static final String JVM_MEMORY_POOL_BYTES_COMMITTED = "jvm_memory_pool_bytes_committed"; 048 private static final String JVM_MEMORY_POOL_BYTES_MAX = "jvm_memory_pool_bytes_max"; 049 private static final String JVM_MEMORY_POOL_BYTES_INIT = "jvm_memory_pool_bytes_init"; 050 private static final String JVM_MEMORY_POOL_COLLECTION_USED_BYTES = "jvm_memory_pool_collection_used_bytes"; 051 private static final String JVM_MEMORY_POOL_COLLECTION_COMMITTED_BYTES = "jvm_memory_pool_collection_committed_bytes"; 052 private static final String JVM_MEMORY_POOL_COLLECTION_MAX_BYTES = "jvm_memory_pool_collection_max_bytes"; 053 private static final String JVM_MEMORY_POOL_COLLECTION_INIT_BYTES = "jvm_memory_pool_collection_init_bytes"; 054 055 private final MemoryMXBean memoryBean; 056 private final List<MemoryPoolMXBean> poolBeans; 057 058 public MemoryPoolsExports() { 059 this( 060 ManagementFactory.getMemoryMXBean(), 061 ManagementFactory.getMemoryPoolMXBeans()); 062 } 063 064 public MemoryPoolsExports(MemoryMXBean memoryBean, 065 List<MemoryPoolMXBean> poolBeans) { 066 this.memoryBean = memoryBean; 067 this.poolBeans = poolBeans; 068 } 069 070 void addMemoryAreaMetrics(List<MetricFamilySamples> sampleFamilies, Predicate<String> nameFilter) { 071 MemoryUsage heapUsage = memoryBean.getHeapMemoryUsage(); 072 MemoryUsage nonHeapUsage = memoryBean.getNonHeapMemoryUsage(); 073 074 if (nameFilter.test(JVM_MEMORY_OBJECTS_PENDING_FINALIZATION)) { 075 GaugeMetricFamily finalizer = new GaugeMetricFamily( 076 JVM_MEMORY_OBJECTS_PENDING_FINALIZATION, 077 "The number of objects waiting in the finalizer queue.", 078 memoryBean.getObjectPendingFinalizationCount()); 079 sampleFamilies.add(finalizer); 080 } 081 082 if (nameFilter.test(JVM_MEMORY_BYTES_USED)) { 083 GaugeMetricFamily used = new GaugeMetricFamily( 084 JVM_MEMORY_BYTES_USED, 085 "Used bytes of a given JVM memory area.", 086 Collections.singletonList("area")); 087 used.addMetric(Collections.singletonList("heap"), heapUsage.getUsed()); 088 used.addMetric(Collections.singletonList("nonheap"), nonHeapUsage.getUsed()); 089 sampleFamilies.add(used); 090 } 091 092 if (nameFilter.test(JVM_MEMORY_BYTES_COMMITTED)) { 093 GaugeMetricFamily committed = new GaugeMetricFamily( 094 JVM_MEMORY_BYTES_COMMITTED, 095 "Committed (bytes) of a given JVM memory area.", 096 Collections.singletonList("area")); 097 committed.addMetric(Collections.singletonList("heap"), heapUsage.getCommitted()); 098 committed.addMetric(Collections.singletonList("nonheap"), nonHeapUsage.getCommitted()); 099 sampleFamilies.add(committed); 100 } 101 102 if (nameFilter.test(JVM_MEMORY_BYTES_MAX)) { 103 GaugeMetricFamily max = new GaugeMetricFamily( 104 JVM_MEMORY_BYTES_MAX, 105 "Max (bytes) of a given JVM memory area.", 106 Collections.singletonList("area")); 107 max.addMetric(Collections.singletonList("heap"), heapUsage.getMax()); 108 max.addMetric(Collections.singletonList("nonheap"), nonHeapUsage.getMax()); 109 sampleFamilies.add(max); 110 } 111 112 if (nameFilter.test(JVM_MEMORY_BYTES_INIT)) { 113 GaugeMetricFamily init = new GaugeMetricFamily( 114 JVM_MEMORY_BYTES_INIT, 115 "Initial bytes of a given JVM memory area.", 116 Collections.singletonList("area")); 117 init.addMetric(Collections.singletonList("heap"), heapUsage.getInit()); 118 init.addMetric(Collections.singletonList("nonheap"), nonHeapUsage.getInit()); 119 sampleFamilies.add(init); 120 } 121 } 122 123 void addMemoryPoolMetrics(List<MetricFamilySamples> sampleFamilies, Predicate<String> nameFilter) { 124 125 boolean anyPoolMetricPassesFilter = false; 126 127 GaugeMetricFamily used = null; 128 if (nameFilter.test(JVM_MEMORY_POOL_BYTES_USED)) { 129 used = new GaugeMetricFamily( 130 JVM_MEMORY_POOL_BYTES_USED, 131 "Used bytes of a given JVM memory pool.", 132 Collections.singletonList("pool")); 133 sampleFamilies.add(used); 134 anyPoolMetricPassesFilter = true; 135 } 136 GaugeMetricFamily committed = null; 137 if (nameFilter.test(JVM_MEMORY_POOL_BYTES_COMMITTED)) { 138 committed = new GaugeMetricFamily( 139 JVM_MEMORY_POOL_BYTES_COMMITTED, 140 "Committed bytes of a given JVM memory pool.", 141 Collections.singletonList("pool")); 142 sampleFamilies.add(committed); 143 anyPoolMetricPassesFilter = true; 144 } 145 GaugeMetricFamily max = null; 146 if (nameFilter.test(JVM_MEMORY_POOL_BYTES_MAX)) { 147 max = new GaugeMetricFamily( 148 JVM_MEMORY_POOL_BYTES_MAX, 149 "Max bytes of a given JVM memory pool.", 150 Collections.singletonList("pool")); 151 sampleFamilies.add(max); 152 anyPoolMetricPassesFilter = true; 153 } 154 GaugeMetricFamily init = null; 155 if (nameFilter.test(JVM_MEMORY_POOL_BYTES_INIT)) { 156 init = new GaugeMetricFamily( 157 JVM_MEMORY_POOL_BYTES_INIT, 158 "Initial bytes of a given JVM memory pool.", 159 Collections.singletonList("pool")); 160 sampleFamilies.add(init); 161 anyPoolMetricPassesFilter = true; 162 } 163 GaugeMetricFamily collectionUsed = null; 164 if (nameFilter.test(JVM_MEMORY_POOL_COLLECTION_USED_BYTES)) { 165 collectionUsed = new GaugeMetricFamily( 166 JVM_MEMORY_POOL_COLLECTION_USED_BYTES, 167 "Used bytes after last collection of a given JVM memory pool.", 168 Collections.singletonList("pool")); 169 sampleFamilies.add(collectionUsed); 170 anyPoolMetricPassesFilter = true; 171 } 172 GaugeMetricFamily collectionCommitted = null; 173 if (nameFilter.test(JVM_MEMORY_POOL_COLLECTION_COMMITTED_BYTES)) { 174 collectionCommitted = new GaugeMetricFamily( 175 JVM_MEMORY_POOL_COLLECTION_COMMITTED_BYTES, 176 "Committed after last collection bytes of a given JVM memory pool.", 177 Collections.singletonList("pool")); 178 sampleFamilies.add(collectionCommitted); 179 anyPoolMetricPassesFilter = true; 180 } 181 GaugeMetricFamily collectionMax = null; 182 if (nameFilter.test(JVM_MEMORY_POOL_COLLECTION_MAX_BYTES)) { 183 collectionMax = new GaugeMetricFamily( 184 JVM_MEMORY_POOL_COLLECTION_MAX_BYTES, 185 "Max bytes after last collection of a given JVM memory pool.", 186 Collections.singletonList("pool")); 187 sampleFamilies.add(collectionMax); 188 anyPoolMetricPassesFilter = true; 189 } 190 GaugeMetricFamily collectionInit = null; 191 if (nameFilter.test(JVM_MEMORY_POOL_COLLECTION_INIT_BYTES)) { 192 collectionInit = new GaugeMetricFamily( 193 JVM_MEMORY_POOL_COLLECTION_INIT_BYTES, 194 "Initial after last collection bytes of a given JVM memory pool.", 195 Collections.singletonList("pool")); 196 sampleFamilies.add(collectionInit); 197 anyPoolMetricPassesFilter = true; 198 } 199 if (anyPoolMetricPassesFilter) { 200 for (final MemoryPoolMXBean pool : poolBeans) { 201 MemoryUsage poolUsage = pool.getUsage(); 202 if (poolUsage != null) { 203 addPoolMetrics(used, committed, max, init, pool.getName(), poolUsage); 204 } 205 MemoryUsage collectionPoolUsage = pool.getCollectionUsage(); 206 if (collectionPoolUsage != null) { 207 addPoolMetrics(collectionUsed, collectionCommitted, collectionMax, collectionInit, pool.getName(), collectionPoolUsage); 208 } 209 } 210 } 211 } 212 213 private void addPoolMetrics(GaugeMetricFamily used, GaugeMetricFamily committed, GaugeMetricFamily max, GaugeMetricFamily init, String poolName, MemoryUsage poolUsage) { 214 if (used != null) { 215 used.addMetric(Collections.singletonList(poolName), poolUsage.getUsed()); 216 } 217 if (committed != null) { 218 committed.addMetric(Collections.singletonList(poolName), poolUsage.getCommitted()); 219 } 220 if (max != null) { 221 max.addMetric(Collections.singletonList(poolName), poolUsage.getMax()); 222 } 223 if (init != null) { 224 init.addMetric(Collections.singletonList(poolName), poolUsage.getInit()); 225 } 226 } 227 228 @Override 229 public List<MetricFamilySamples> collect() { 230 return collect(null); 231 } 232 233 @Override 234 public List<MetricFamilySamples> collect(Predicate<String> nameFilter) { 235 List<MetricFamilySamples> mfs = new ArrayList<MetricFamilySamples>(); 236 addMemoryAreaMetrics(mfs, nameFilter == null ? ALLOW_ALL : nameFilter); 237 addMemoryPoolMetrics(mfs, nameFilter == null ? ALLOW_ALL : nameFilter); 238 return mfs; 239 } 240}