Java APM and JVM monitoring
Attach the OpenTelemetry Java agent and Obseria shows request traces, JVM heap, garbage collection, threads and CPU profiles for every Java service, correlated with logs and infrastructure.


Why monitor Java with Obseria
Zero-code instrumentation
The OpenTelemetry Java agent instruments Spring, Servlet containers, JDBC, Kafka, gRPC and HTTP clients at startup, without touching your code.
JVM internals in context
Heap pools, GC pauses and thread counts sit next to the traces they slow down, so you see whether latency comes from code, the JVM or a dependency.
Down to the method
Continuous profiling with under 1% overhead shows which methods burn CPU or allocate memory in production.
Java frameworks and runtimes Obseria monitors
- Spring Boot and Spring MVC/WebFlux. Controllers, RestTemplate, WebClient and Spring Data calls appear as spans automatically.
- Jakarta EE and servlet containers. Tomcat, Jetty, Undertow, WildFly and other servlet containers are traced at the request level.
- Quarkus and Micronaut. Cloud-native frameworks are covered through the agent or their built-in OpenTelemetry extensions.
- JDBC, Hibernate and connection pools. Every SQL statement becomes a client span, and HikariCP pool usage is exposed as metrics.
- Kafka, JMS and RabbitMQ clients. Producer and consumer spans carry trace context across queues for end-to-end messaging traces.
- HotSpot, OpenJDK and GraalVM JVMs. Runtime metrics follow the OpenTelemetry JVM semantic conventions on Java 8 and newer.
The Java signals that matter
Obseria collects these values automatically and shows them in ready-made dashboards and alerts.User-facing speed
Shows how quickly your Java services answer requests, the number that matters most to your customers.
Memory health
Tells you how much memory your application uses and whether it is creeping toward its limit over time.
Cleanup pauses
Reveals how long the application pauses to free up memory, which users experience as sudden slow moments.
Processing load
Shows how busy your application is and how many tasks and components it is currently running.
Show all 8 metrics in detail
| Metric | Unit | What it tells you |
|---|---|---|
jvm.memory.used | By | Memory used per pool (Eden, Survivor, Old Gen, Metaspace); the basis for leak detection. |
jvm.memory.used_after_last_gc | By | Live heap after collection; a steady climb points to a memory leak. |
jvm.memory.limit | By | Maximum size of each pool, to compute heap utilization against -Xmx. |
jvm.gc.duration | s | Duration of garbage collection runs by collector and action; long pauses hit latency. |
jvm.thread.count | {thread} | Live threads by state; blocked or exploding thread counts reveal contention. |
jvm.class.loaded | {class} | Classes loaded since start; unexpected growth hints at classloader leaks. |
jvm.cpu.recent_utilization | 1 | Recent CPU usage of the JVM process, to separate CPU saturation from waiting. |
http.server.request.duration | s | Latency histogram of inbound HTTP requests per route and status code. |
Set up Java monitoring in minutes
- Step 1
Download the Java agent
Fetch the official opentelemetry-javaagent.jar or install the Obseria Agent, which can inject it on hosts and Kubernetes pods.
- Step 2
Point it to Obseria
Set OTEL_SERVICE_NAME, the OTLP endpoint and your token as environment variables.
- Step 3
Start with -javaagent
Add the -javaagent flag to the JVM start command; traces and JVM metrics arrive within minutes.
# Download the official OpenTelemetry Java agent
curl -L -o opentelemetry-javaagent.jar \
https://github.com/open-telemetry/opentelemetry-java-instrumentation/releases/latest/download/opentelemetry-javaagent.jar
export OTEL_SERVICE_NAME="checkout"
export OTEL_RESOURCE_ATTRIBUTES="deployment.environment.name=production,service.version=1.8.0"
export OTEL_EXPORTER_OTLP_ENDPOINT="${OBSERIA_OTLP_ENDPOINT}"
export OTEL_EXPORTER_OTLP_HEADERS="Authorization=Bearer%20${OBSERIA_TOKEN}"
export OTEL_EXPORTER_OTLP_PROTOCOL="http/protobuf"
java -javaagent:./opentelemetry-javaagent.jar -jar checkout.jarOut of the box
Dashboards, alerts and SLOs that are ready the moment data arrives.
- JVM overview: heap, GC, threads, classes
- Service health with RED metrics per endpoint
- Alert on GC pause time above threshold
- Alert on heap after GC trending upward
- Slowest SQL statements by service
Java monitoring: what matters
Why Java APM matters
Java still runs a large share of business-critical backends: payment systems, order processing, banking cores and countless Spring Boot microservices. These services are fast when they are healthy, but the JVM adds a layer between your code and the operating system. Garbage collection, JIT compilation, thread pools and connection pools all influence response times. Java APM combines distributed traces with JVM monitoring so you can tell whether a slow request is caused by your code, by the runtime or by a downstream database.
Classic Java performance monitoring relied on proprietary bytecode agents. Today the OpenTelemetry Java agent provides the same zero-code approach as an open standard: one jar, attached with -javaagent, instruments hundreds of libraries and emits traces, metrics and logs over OTLP.
JVM signals that matter
A useful Java performance monitoring setup watches a small set of runtime signals alongside the golden signals of each service:
- Heap and memory pools:
jvm.memory.usedper pool andjvm.memory.used_after_last_gc. The second one matters most, because live data after collection shows the real memory footprint. - Garbage collection:
jvm.gc.durationby collector (G1, ZGC, Parallel). Frequent or long pauses translate directly into tail latency. - Threads:
jvm.thread.countby state. Many blocked threads point to lock contention or exhausted pools. - CPU:
jvm.cpu.recent_utilizationtells you whether the process is CPU-bound or waiting on I/O. - Requests and dependencies:
http.server.request.durationplus database and messaging client spans give rate, errors and duration per endpoint.
Common Java performance problems and how to detect them
Memory leaks
A leak shows up as heap after GC rising step by step across hours or days, followed by longer collections and eventually an OutOfMemoryError. Alerting on the trend rather than on a fixed threshold catches it before users notice.
GC pressure
High allocation rates force frequent young-generation collections. Traces with gaps that line up with GC events reveal the impact; allocation profiles show which code paths allocate the most.
Pool exhaustion
When a JDBC or HTTP client pool is too small, requests queue up. You see rising latency, many waiting threads and spans that spend time before the actual query starts.
Slow queries and N+1 patterns
ORM frameworks such as Hibernate can issue dozens of statements per request. Trace waterfalls make N+1 patterns obvious at a glance.
Lock contention and stuck threads
Synchronized blocks around shared caches or legacy singletons serialize requests under load. A rising number of threads in the BLOCKED state, together with CPU that stays flat while latency grows, is the typical fingerprint. Lock profiles and thread dumps taken at that moment show which monitor everyone waits for.
Slow startup and cold JIT
Right after a deployment, the JIT compiler has not optimized hot paths yet, and the first requests are slower. Comparing latency per service.version over the first minutes after rollout separates warm-up effects from real regressions.
Best practices for JVM monitoring
- Set
service.name,service.versionanddeployment.environment.nameon every service so you can compare releases. - Prefer the agent for broad coverage and add manual spans with the OpenTelemetry API only for business logic that matters.
- Watch heap after GC and GC duration together; either one alone can mislead.
- Keep container memory limits and
-Xmxaligned, and monitor both the JVM and the cgroup. - Use tail-based sampling to keep all slow and failed traces while dropping routine ones.
How Obseria helps with Java APM
Obseria accepts the standard OpenTelemetry Java agent output over OTLP, so there is no proprietary agent to maintain. On hosts and Kubernetes, the Obseria Agent can inject the Java agent automatically and adds host, container and log data. TopoGraph maps each Java service to its databases, queues and callers. Continuous profiling with under 1% overhead links hot methods to the traces they slow down. When latency rises, Obseria Intelligence correlates a GC spike, a deployment or a slow query and presents the root cause with its chain of evidence. Dashboards, alerts and SLOs for Java services are included, and all data stays exportable via OTLP.
Related integrations
Related capabilities
Distributed Tracing
Follow each request across services, queues and databases with OpenTelemetry-native distributed tracing, and find the slow span or failing dependency in seconds.
Learn moreContinuous Profiling
Always-on continuous profiling shows which functions consume CPU, memory and locks in production, with under 1% overhead and direct links to traces.
Learn moreError Tracking
The Errors Inbox groups exceptions from backend, frontend and mobile apps into issues, shows their impact and lets teams assign and resolve them in one workflow.
Learn moreLearn more
What is APM (application performance monitoring)?
What is APM? Learn how application performance monitoring measures latency, throughput and errors, how distributed tracing works, and which metrics matter.
What is OpenTelemetry?
What is OpenTelemetry? Learn how OTel's APIs, SDKs, Collector, OTLP and semantic conventions produce vendor-neutral traces, metrics, logs and profiles.
Java monitoring FAQ
No. The OpenTelemetry Java agent instruments common frameworks and libraries at startup. You only add the -javaagent flag and a few environment variables. Manual spans are optional.
The OpenTelemetry Java agent supports Java 8 and newer on HotSpot, OpenJDK and other common JVMs. Obseria ingests whatever the agent emits over OTLP.
Overhead depends on traffic and the number of instrumented libraries. Sampling and disabling unneeded instrumentations keep it low. Continuous profiling in Obseria runs at under 1% overhead.
Yes. The Java agent emits JVM runtime metrics by default, and you can collect additional MBeans, for example from Kafka or Tomcat, with the OpenTelemetry JMX metrics support or the Collector.
Yes. Deploy the Obseria Agent as a DaemonSet via Helm, or use the OpenTelemetry Operator to inject the Java agent into pods. Traces are enriched with pod, namespace and node metadata.
Start monitoring Java today
Connect Java in minutes with OpenTelemetry or the Obseria Agent. Free for 14 days.