{}const=>[]async()letfn</>var
DevelopmentPractice

JVM optimization: from a slow application to a combat production in 30 minutes

In this detailed guide, we will analyze how to properly configure the JVM, select a garbage collector, profile the application and prepare it for real loads. Practical examples, ready-made configurations and a checklist for production — everything a novice developer needs.

К

Kodik

Author

5 min read

JVM Memory Anatomy: Understanding Heap.

Heap is a memory area where all the objects of your application are stored. It is divided into several areas:

Heap structure:

Young Generation:

  • Eden Space - new objects are created here

  • Survivor Space (S0 and S1) - objects that survived the first garbage collection

Old Generation:

  • Long-lived objects that have survived several garbage collection cycles

Metaspace (replaced PermGen in Java 8+):

  • Class metadata, constants

How do I configure the Heap size?

# Set the minimum and maximum heap size
java -Xms2g -Xmx4g -jar myapp.jar

# -Xms — initial heap size (2 GB)
# -Xmx — maximum heap size (4 GB)

Important rule: Xms and Xmx are better to be made the same in production to avoid changing the heap size during operation.

How to calculate the right size?

To begin with, you can use the formula:

Heap Size = (Peak Live Data Size) × (2-4)

Where Peak Live Data Size is the maximum amount of "live" objects after Full GC.

🔥 100,000+ students already with us

Tired of reading theory?
Time to code!

Kodik — an app where you learn to code through practice. AI mentor, interactive lessons, real projects.

🤖 AI 24/7
🎓 Certificates
💰 Free
🚀 Start learning
Joined today

Garbage Collection: select and configure

GC (Garbage Collector) automatically removes unused objects from memory. There are several types of garbage collectors, each with its own characteristics.

Main types of GC:

1. Serial GC (for small applications)

java -XX:+UseSerialGC -jar myapp.jar

When to use: Single-threaded applications, heap < 100 MB

2. Parallel GC (default in Java 8)

java -XX:+UseParallelGC -jar myapp.jar

When to use: Applications where throughput is important can tolerate pauses

3. G1GC (recommended for most cases)

java -XX:+UseG1GC -jar myapp.jar

When to use: Heap > 4 GB, predictable pauses are needed

Setting the target pause time:

java -XX:+UseG1GC -XX:MaxGCPauseMillis=200 -jar myapp.jar

4. ZGC and Shenandoah (for low-latency applications)

# ZGC (Java 11+, production-ready with Java 15)
java -XX:+UseZGC -jar myapp.jar

# Shenandoah
java -XX:+UseShenandoahGC -jar myapp.jar

When to use: Critical minimum pauses (< 10 ms), large heap

GC monitoring in logs

Enable detailed GC logs:

java -Xlog:gc*:file=gc.log:time,uptime:filecount=5,filesize=100m \
     -XX:+UseG1GC \
     -jar myapp.jar

Profiling: finding bottlenecks

Profiling helps you understand where your app is spending time and memory.

1. Use JVisualVM (free)

JVisualVM is included in the JDK and allows you to:

  • Monitor CPU and memory usage in real time

  • Take heap dumps

  • Analyze flows

# Run the application with JMX
java -Dcom.sun.management.jmxremote \
     -Dcom.sun.management.jmxremote.port=9010 \
     -Dcom.sun.management.jmxremote.authenticate=false \
     -Dcom.sun.management.jmxremote.ssl=false \
     -jar myapp.jar

Then connect via VisualVM to localhost:9010.

2. Heap Dump Analysis

When an OutOfMemoryError occurs, it is useful to get a memory snapshot:

# Automatic dump at OOM
java -XX:+HeapDumpOnOutOfMemoryError \
     -XX:HeapDumpPath=/tmp/heapdump.hprof \
     -jar myapp.jar

# Or create manually
jmap -dump:live,format=b,file=heap.bin <PID>

Analyze the dump in Eclipse MAT or VisualVM, look for:

  • Objects that take up the most memory

  • Memory leaks

  • Unexpectedly large collections

3. Async Profiler (production-ready)

Async-profiler is suitable for production — it does not slow down the application:

# Download
wget https://github.com/jvm-profiling-tools/async-profiler/releases/latest/download/async-profiler-2.9-linux-x64.tar.gz

# Run profiling for 60 seconds
./profiler.sh -d 60 -f flamegraph.html <PID>

The result is a flamegraph, where you can see the "hot" methods.

4. Monitoring metrics

Use Micrometer + Prometheus + Grafana:

// Spring Boot automatically exports JVM metrics
@SpringBootApplication
public class Application {
    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
}

Add to application.properties:

management.endpoints.web.exposure.include=prometheus,health,metrics
management.metrics.export.prometheus.enabled=true

Typical problems and their solutions

Problem 1: Frequent Full GC

Symptoms: The app "freezes" for seconds

Solution:

# Increase heap or configure young generation
java -Xms4g -Xmx4g \
     -XX:NewRatio=2 \
     -XX:+UseG1GC \
     -jar myapp.jar

Problem 2: OutOfMemoryError: Java heap space

Solution:

  1. Increase -Xmx

  2. Find memory leaks through heap dump

  3. Optimize the code (get rid of unnecessary collections)

Problem 3: OutOfMemoryError: Metaspace

Solution:

# Increase metaspace
java -XX:MetaspaceSize=256m -XX:MaxMetaspaceSize=512m -jar myapp.jar

Problem 4: High CPU load due to GC

Solution:

  • Switch to a more efficient GC (G1, ZGC)

  • Increase heap

  • Optimize object creation in code

Checklist for preparation for production

1. Memory settings

java -Xms4g -Xmx4g \                    # Fixed heap
     -XX:MetaspaceSize=256m \           # Metaspace
     -XX:MaxMetaspaceSize=512m \
     -XX:+UseG1GC \                     # G1 GC
     -XX:MaxGCPauseMillis=200 \         # Target pause time
     -XX:+HeapDumpOnOutOfMemoryError \  # Dump at OOM
     -XX:HeapDumpPath=/var/log/app \
     -jar myapp.jar

2. GC logging

-Xlog:gc*:file=/var/log/app/gc.log:time,uptime:filecount=5,filesize=100m

3. JMX for monitoring

-Dcom.sun.management.jmxremote \
-Dcom.sun.management.jmxremote.port=9010 \
-Dcom.sun.management.jmxremote.authenticate=true \
-Dcom.sun.management.jmxremote.ssl=true \
-Dcom.sun.management.jmxremote.password.file=/etc/jmx.password

4. Containerization (Docker)

JVM automatically detects container limits with Java 10+:

FROM openjdk:17-slim

# JVM will automatically see the limits
ENV JAVA_OPTS="-XX:+UseContainerSupport \
               -XX:MaxRAMPercentage=75.0 \
               -XX:+UseG1GC"

CMD java $JAVA_OPTS -jar app.jar
# docker-compose.yml
services:
  app:
    image: myapp:latest
    deploy:
      resources:
        limits:
          memory: 2G

5. Monitoring in production

Be sure to keep track of:

  • Heap usage (used memory)

  • GC frequency (frequency of assemblies)

  • GC pause time (duration of pauses)

  • Thread count (number of streams)

  • CPU usage

Example of a ready-made configuration

For a typical Spring Boot application:

#!/bin/bash
java -Xms2g -Xmx2g \
     -XX:MetaspaceSize=256m \
     -XX:MaxMetaspaceSize=256m \
     -XX:+UseG1GC \
     -XX:MaxGCPauseMillis=200 \
     -XX:+HeapDumpOnOutOfMemoryError \
     -XX:HeapDumpPath=/var/log/myapp/heapdump.hprof \
     -Xlog:gc*:file=/var/log/myapp/gc.log:time,uptime:filecount=10,filesize=100m \
     -Dcom.sun.management.jmxremote \
     -Dcom.sun.management.jmxremote.port=9010 \
     -Dcom.sun.management.jmxremote.authenticate=false \
     -Dcom.sun.management.jmxremote.ssl=false \
     -jar myapp.jar

Additional tools

  • GCEasy (gceasy.io) — online GC log analysis

  • FastThread (fastthread.io) — thread dumps analysis

  • JProfiler - commercial profiler

  • YourKit — another great commercial profiler

  • Arthas — Alibaba's runtime diagnostics tool

Conclusions

JVM optimization is an iterative process. Start by understanding the basic concepts (heap, GC, profiling), use the right tools for diagnostics, and gradually optimize for your specific workload.

Remember: Premature optimization is the root of all evil. Measure first, then optimize!

Was it helpful?

Join Codice - an educational platform for developers!

Here you will find structured courses, practical tasks and up-to-date materials on Java, performance optimization and preparation for production.

And we also have cool Telegram channel with a friendly community, where experienced developers share knowledge, help solve problems, and discuss best practices. Join us!

🎯Stop procrastinating

Liked the article?
Time to practice!

In Kodik, you don't just read — you write code immediately. Theory + practice = real skills.

Instant practice
🧠AI explains code
🏆Certificate

No registration • No card