Article
In the world of cloud-native Java, "you can't manage what you can't measure" is a fundamental truth. While the JVM provides a wealth of performance data through Java Management Extensions (JMX), this data is often locked away and difficult to collect at scale. The Prometheus JMX Exporter is a dedicated tool that bridges this gap, transforming internal JVM metrics into a Prometheus-friendly format, giving you deep visibility into your application's health and performance.
What is the Prometheus JMX Exporter?
The Prometheus JMX Exporter is a Java agent that can be attached to any Java application. It serves two primary functions:
- JMX Scraping: It queries the application's built-in JMX MBeans (Management Beans) which expose critical JVM and application-level metrics.
- HTTP Endpoint: It exposes these metrics on an HTTP endpoint (typically
/metrics) in the plain-text format that Prometheus expects.
This allows your Prometheus server to regularly "scrape" this endpoint and collect the time-series data for alerting and graphing.
Why is it Essential for Java Applications?
The JMX Exporter provides immediate, out-of-the-box visibility into the core health of your JVM, which is crucial for:
- Performance Diagnostics: Identifying memory leaks, GC pressure, and thread pool exhaustion.
- Capacity Planning: Understanding memory and CPU usage trends over time.
- Proactive Alerting: Setting up alerts for impending problems (e.g., old generation memory filling up, rising GC duration).
- Correlation: Correlating application performance (e.g., high API latency) with underlying JVM state (e.g., a garbage collection pause).
How to Use the JMX Exporter
There are two primary modes of operation: as a Java Agent (recommended) or as an HTTP Server (for standalone export).
Method 1: As a Java Agent (Most Common)
This is the simplest and most robust method. You add the agent to the JVM's startup command.
- Download the JAR:
wget https://repo1.maven.org/maven2/io/prometheus/jmx/jmx_prometheus_javaagent/0.19.0/jmx_prometheus_javaagent-0.19.0.jar - Create a Configuration File: This YAML file defines which MBeans to scrape and how to convert them into Prometheus metrics. You can start with a comprehensive default configuration.
# jmx-exporter-config.yaml rules: # Special rule to extract JVM classloading info - pattern: "java.lang<type=ClassLoader><>(.*):" name: "java_lang_classloader_$1" # Special rule for JVM Memory Pool usage - pattern: "java.lang<type=MemoryPool><name=([^>]+)><>(.*):" name: "jvm_memory_pool_$2" labels: pool: "$1" # Capture garbage collection stats - pattern: "java.lang<type=GarbageCollector><name=([^>]+)><>(.*):" name: "jvm_garbage_collector_$2" labels: gc: "$1" # Capture thread state counts - pattern: "java.lang<type=Threading><>(.*):" name: "jvm_threading_$1" - Start Your Java Application with the Agent:
bash java -javaagent:./jmx_prometheus_javaagent-0.19.0.jar=8080:jmx-exporter-config.yaml \ -jar my-spring-boot-app.jar
This starts the exporter on port8080. Your metrics will be available athttp://localhost:8080/metrics.
In a Dockerfile, this would look like:
FROM eclipse-temurin:11-jre # Copy the application JAR COPY target/my-spring-boot-app.jar /app/app.jar # Copy the JMX Exporter Agent and its config COPY jmx-exporter/jmx_prometheus_javaagent-0.19.0.jar /app/jmx_agent.jar COPY jmx-exporter/config.yaml /app/jmx_config.yaml # Expose the metrics port EXPOSE 8080 # Run the application with the agent ENTRYPOINT ["java", "-javaagent:/app/jmx_agent.jar=8080:/app/jmx_config.yaml", "-jar", "/app/app.jar"]
Key JVM Metrics Exposed
Once running, the exporter provides dozens of critical metrics. Here are the most important ones for a Java developer:
- Memory:
jvm_memory_bytes_used{area="heap"}: Current heap memory usage.jvm_memory_bytes_max{area="non-heap"}: Max non-heap memory available.
- Garbage Collection:
jvm_garbage_collector_collection_seconds_count{gc="G1 Young Generation"}: Total count of Young Gen GCs.jvm_garbage_collector_collection_seconds_sum{gc="G1 Old Generation"}: Total time spent in Old Gen GC.
- Threads:
jvm_threads_live: Current live threads (both daemon and non-daemon).jvm_threads_state{state="BLOCKED"}: Number of threads in a BLOCKED state (a key indicator of contention).
- Class Loading:
jvm_classes_loaded: The number of classes currently loaded.
- Buffer Pools:
jvm_buffer_pool_used_bytes{name="direct"}: Memory used by direct buffers (critical for NIO applications).
Exposing Custom Application Metrics
The real power comes from exposing your own business and application metrics. If you register your own MBeans, the JMX Exporter can scrape them too.
1. Create a Custom MBean Interface:
public interface OrderServiceMBean {
int getOrderCount();
long getTotalRevenue();
void reset(); // For testing
}
2. Implement the MBean:
@Component
public class OrderService implements OrderServiceMBean {
private final AtomicInteger orderCount = new AtomicInteger();
private final AtomicLong totalRevenue = new AtomicLong();
public void processOrder(Order order) {
// ... business logic
orderCount.incrementAndGet();
totalRevenue.addAndGet(order.getAmount());
}
@Override
public int getOrderCount() { return orderCount.get(); }
@Override
public long getTotalRevenue() { return totalRevenue.get(); }
@Override
public void reset() {
orderCount.set(0);
totalRevenue.set(0);
}
}
3. Register the MBean with the JMX Server:
import javax.management.*;
@Component
public class OrderServiceMBeanRegistrar {
public OrderServiceMBeanRegistrar(MBeanServer mBeanServer, OrderService orderService) throws Exception {
ObjectName objectName = new ObjectName("com.example.app:type=OrderService,name=orderMetrics");
mBeanServer.registerMBean(orderService, objectName);
}
}
4. Add a Rule to Your JMX Exporter Config:
# jmx-exporter-config.yaml rules: # ... (previous JVM rules) - pattern: 'com.example.app<type=OrderService, name=orderMetrics><>(.*):' name: "myapp_orders_$1" type: GAUGE
Now, your custom metrics myapp_orders_OrderCount and myapp_orders_TotalRevenue will be exposed alongside the standard JVM metrics.
Prometheus Configuration
Finally, you need to tell Prometheus to scrape your application.
# prometheus.yml scrape_configs: - job_name: 'my-java-app' static_configs: - targets: ['host.docker.internal:8080'] # Or your app's k8s service name metrics_path: /metrics scrape_interval: 15s
Conclusion
The Prometheus JMX Exporter is a non-negotiable tool for any serious Java deployment. It provides a seamless bridge between the deep introspection capabilities of the JVM and the powerful monitoring and alerting ecosystem of Prometheus. By implementing it, you move from guessing about your application's performance to having a precise, data-driven understanding of its inner state, enabling you to build more reliable, performant, and observable Java services.
Pyroscope Profiling in Java
Explains how to use Pyroscope for continuous profiling in Java applications, helping developers analyze CPU and memory usage patterns to improve performance and identify bottlenecks.
https://macronepal.com/blog/pyroscope-profiling-in-java/
OpenTelemetry Metrics in Java: Comprehensive Guide
Provides a complete guide to collecting and exporting metrics in Java using OpenTelemetry, including counters, histograms, gauges, and integration with monitoring tools. (MACRO NEPAL)
https://macronepal.com/blog/opentelemetry-metrics-in-java-comprehensive-guide/
OTLP Exporter in Java: Complete Guide for OpenTelemetry
Explains how to configure OTLP exporters in Java to send telemetry data such as traces, metrics, and logs to monitoring systems using HTTP or gRPC protocols. (MACRO NEPAL)
https://macronepal.com/blog/otlp-exporter-in-java-complete-guide-for-opentelemetry/
Thanos Integration in Java: Global View of Metrics
Explains how to integrate Thanos with Java monitoring systems to create a scalable global metrics view across multiple Prometheus instances.
https://macronepal.com/blog/thanos-integration-in-java-global-view-of-metrics
Time Series with InfluxDB in Java: Complete Guide (Version 2)
Explains how to manage time-series data using InfluxDB in Java applications, including storing, querying, and analyzing metrics data.
https://macronepal.com/blog/time-series-with-influxdb-in-java-complete-guide-2
Time Series with InfluxDB in Java: Complete Guide
Provides an overview of integrating InfluxDB with Java for time-series data handling, including monitoring applications and managing performance metrics.
https://macronepal.com/blog/time-series-with-influxdb-in-java-complete-guide
Implementing Prometheus Remote Write in Java (Version 2)
Explains how to configure Java applications to send metrics data to Prometheus-compatible systems using the remote write feature for scalable monitoring.
https://macronepal.com/blog/implementing-prometheus-remote-write-in-java-a-complete-guide-2
Implementing Prometheus Remote Write in Java: Complete Guide
Provides instructions for sending metrics from Java services to Prometheus servers, enabling centralized monitoring and real-time analytics.
https://macronepal.com/blog/implementing-prometheus-remote-write-in-java-a-complete-guide
Building a TileServer GL in Java: Vector and Raster Tile Server
Explains how to build a TileServer GL in Java for serving vector and raster map tiles, useful for geographic visualization and mapping applications.
https://macronepal.com/blog/building-a-tileserver-gl-in-java-vector-and-raster-tile-server
Indoor Mapping in Java
Explains how to create indoor mapping systems in Java, including navigation inside buildings, spatial data handling, and visualization techniques.