Java Memory Options You Need in Production

 

Transcript:

So, you are ready to tune the memory footprint of your Java application. Congrats. There are more than 500 flags at your service. Well, actually, no worries. You don't need all of them. Let's look at the 35 most essential JVM memory configuration options and when to use them.

Let's first look at heap sizing options. The heap is your first frontier in JVM tuning because it directly affects how often the GC works and how much work it does. Too small a heap means frequent GC work because you'll run out of space sooner. That steals CPU time from your actual application. Too large a heap means fewer collections, but potentially bigger or less predictable pauses and a bigger memory footprint.

Luckily, in some cases, if you just set the maximum and minimum heap size, it will be enough to optimize the JVM memory footprint. The optimal heap size, of course, depends on your application, so you should experiment with the values before settling on the final number.

You can also set the minimum and maximum percentage of free space that should be left after garbage collection. This helps control dynamic heap sizing. For instance, if free space is below the minimum free space ratio, the JVM tries to grow the heap up to the maximum size. If free space is above the maximum free space ratio, the JVM tries to shrink the heap down to the minimum size.

With the MaxDirectMemorySize option, you can set the size of direct buffer allocations. Direct buffers live outside of the Java heap, but they are still real memory that your process consumes. So you can run out of memory even if your heap looks fine. If the JVM hits this limit, you can get an OutOfMemoryError. This is common in networking or NIO-heavy applications. By default, the JVM chooses the size of direct buffers automatically based on the available memory, so setting MaxDirectMemorySize may help prevent excessive resource consumption.

The JVM flags adjusting the heap size do not affect the total memory consumption of the JVM. The Java process uses RAM for more than just the heap. It also uses metaspace, thread stacks, code cache, GC native structures, direct buffers, and so on. To limit the total RAM consumption, you should set MaxRAMPercentage. The heap size will be adjusted accordingly. For instance, if you have 1 GB of memory and set MaxRAMPercentage to 50, it will make the JVM allocate approximately 500 MB to the heap.

These arguments are especially useful for containerized applications. There, they help adjust RAM consumption and heap size based on the available container memory.

The Java platform offers seven garbage collectors. The default garbage collection settings are enough for many applications, but if you want to improve some KPIs, in our case memory consumption, you can try switching to another garbage collector. Maybe its defaults will be more beneficial for your application. Actually, in most cases, you don't have to dig into the intricacies of GC tuning. Simply switching to another collector might be enough.

Each Java garbage collector is tailored to specific purposes. We have a dedicated Java garbage collection video on this channel, so I'm not going to go into the detailed description of each garbage collector. You can watch that video for reference.

Okay, maybe you want to stay with the default G1GC garbage collector, or maybe you have switched to another one. Again, the default settings of each collector might be more than sufficient for your application. But if you really want to tune them, you have to learn to understand the behavior of your garbage collector. GC logs will help you with that.

GC logs are text files that provide exhaustive information about GC work: total GC time, memory reclamation, allocation, and so on. Note that GC logging parameters vary between Java 8 and Java 9 and newer versions. For example, PrintGCDetails and Xlog:gc in Java 9 and newer substitute PrintGC in Java 8.

Java 8 includes the UseGCLogFileRotation parameter that enables rotation of GC logs. It is used together with NumberOfGCLogFiles and GCLogFileSize. But these flags were replaced in newer Java versions. Also, a new unified GC logging system was implemented with JEP 271. To learn more about the new logging syntax, run Xlog:help.

Also, some important notes. Don't hurry to change the GC implementation. Use the default G1GC and turn on unified logging early. Learn to understand it. After that, if you conclude that the GC implementation is the problem, you can try switching to another implementation.

Only switch to ZGC or Shenandoah GC when you have a real latency problem and can prove it with logs and metrics. Also, Serial GC will be used automatically if the RAM limit is set to less than 1792 MB or there are fewer than two CPUs, even if you enable another collector explicitly, so be careful about that.

If you switch to another collector and are still not entirely satisfied with the memory footprint, and you know that GC is to blame, you can try adjusting its settings. Each GC comes with numerous settings that enable developers to adjust latency, throughput, and memory consumption.

GC management flags are like JVM autopilot knobs: how hard it tries to hit the throughput goals, how quickly it reacts to changes, how many GC worker threads it uses, and in the case of G1GC, when it starts old-generation background work and how it chops the heap into regions.

Here are some important options. GCTimeRatio helps tune throughput. It hints at how much the JVM should spend on GC versus running the application. By default, GCTimeRatio is set to 99. It means that the application gets 99% of the total execution time and the collector can run for no more than 1% of that time. But you can tune this behavior.

ParallelGCThreads sets the number of stop-the-world GC worker threads. You can consider tuning this option in case of real CPU contention. G1HeapRegionSize sets the region size for G1GC. Don't touch it unless you have real evidence that the default region size affects performance. It's usually relevant in scenarios with a very large heap, where the default region sizing does not provide good behavior.

OutOfMemoryError leads to an application crash. This error happens when the JVM can't allocate the memory it needs. This error is hard to troubleshoot, but some JVM parameters provide developers with a lot of information about it, making it easier to detect memory leaks. These parameters do not prevent OutOfMemoryError. They just make it diagnosable.

The best evidence for a heap-related OOM is the heap dump. HeapDumpOnOutOfMemoryError tells HotSpot to automatically write a heap dump when allocation from the Java heap cannot be satisfied. A heap dump is a snapshot of the heap at the time of failure: objects, sizes, references. Enable it anywhere you might want to debug, especially in production.

HeapDumpPath sets where the heap dump should be written, either a directory or a specific file name. OnOutOfMemoryError runs a command or several commands when the first OutOfMemoryError is thrown. This is useful because when you hit OOM, you often want to automatically dump diagnostics, alert someone, or trigger a restart workflow, tag and ship evidence somewhere. HotSpot added this option because OOM is common enough at scale that automatic diagnostics are valuable.

There are some other JVM memory tuning parameters you might find useful. For example, strings take a significant part of application memory. The UseStringDeduplication parameter helps remove duplicate strings from the heap and release resources.

The LargePageHeapSizeThreshold and LargePageSizeInBytes options enable you to operate with large pages. It is a technique to reduce pressure on the processor's translation lookaside buffer cache. This way, you can make better use of physical hardware resources.

TieredCompilation and TieredStopAtLevel=1 can be used to turn off the optimizing compiler and thus reduce memory footprint in some cases. Use them when memory consumption is the only important KPI.

Memory for thread stacks is allocated outside of the heap, so it is not affected by heap size parameters. The ThreadStackSize flag enables developers to reduce the size of thread stacks.

And these were the key Java memory options. I have created a PDF with these options so you can download it and use it as a reference. If you found this video useful, don't forget to like it, subscribe to our channel, and until next time.

Summary

In this video, the most important JVM memory tuning options are explained, with a focus on heap sizing, direct memory, total RAM limits, garbage collection, and OutOfMemoryError diagnostics. The text shows that effective JVM tuning is not about memorizing hundreds of flags, but about understanding a smaller set of practical options and using them in the right situations. It also emphasizes the importance of GC logs, careful collector selection, and evidence-based tuning rather than random configuration changes. Special attention is given to containerized environments, where options like MaxRAMPercentage become especially useful. Overall, the video presents JVM memory tuning as a balance between performance, stability, and controlled resource usage.

About Catherine

Java developer passionate about Spring Boot. Writer. Developer Advocate at BellSoft

Social Media

Videos
card image
Feb 27, 2026
Spring Developer Roadmap 2026: What You Need to Know

Spring Boot is powerful. But knowing the framework isn’t the same as understanding backend engineering. In this video, I walk through the roadmap I believe matters for a Spring developer in 2026. We start with data. That means real SQL — CTEs, window functions, normalization trade-offs — and understanding what ACID and BASE actually imply for system guarantees. Spring Data JPA is useful, but you still need to know what happens underneath. Then architecture: microservices vs modular monolith, serverless, CQRS, and when HTTP, gRPC, Kafka, or WebSockets make sense. Not as buzzwords — but as design choices with trade-offs. Security and infrastructure follow: OWASP Top 10, AuthN vs AuthZ, encryption in transit and at rest, Docker, Kubernetes, Infrastructure as Code, and observability with Micrometer, OpenTelemetry, and Grafana. This roadmap isn’t about mastering every tool. It’s about knowing what affects reliability in production.

Videos
card image
Feb 18, 2026
Build Typed AI Agents in Java with Embabel

Most Java AI demos stop at prompt loops. That doesn't scale in production. In this video, we integrate Embabel into an existing Spring Boot application and build a multi-step, goal-driven agent for incident triage. Instead of manually orchestrating prompt → tool → prompt cycles, we define typed actions and let the agent plan across deterministic and LLM-powered steps. We parse structured input with Ollama, query MongoDB deterministically, classify risk using explicit thresholds, rank affected implants, generate a constrained root cause hypothesis, and produce a bounded containment plan. LLM handles reasoning. Java enforces rules. This is about controlled AI workflows on the JVM — not prompt glue code.

Further watching

Videos
card image
Mar 26, 2026
Java Developer Roadmap 2026: From Basics to Production

Most Java roadmaps teach tools. This one teaches order — the only thing that actually gets you to production. You don’t need to learn everything. You need to learn the right things, in the right sequence. In this video, we break down a practical Java developer roadmap for 2026 — from syntax and OOP to Spring, databases, testing, and deployment. Structured into 8 levels, it shows how real engineers grow from fundamentals to production-ready systems. We cover what to learn and what to ignore: core Java, collections, streams, build tools, Git, SQL and JDBC before Hibernate, the Spring ecosystem, testing with JUnit, and deployment with Docker and CI/CD. You’ll also understand why most developers get stuck — jumping into frameworks too early, skipping SQL, or treating tools as knowledge. This roadmap gives you a clear path into real-world Java development — with priorities, trade-offs, and production context.

Videos
card image
Mar 19, 2026
TOP-5 Lightweight Linux Distributions for Containers

In this video, we compare five lightweight Linux distributions commonly used as base images: Alpine, Alpaquita, Chiseled Ubuntu, RHEL UBI Micro, and Wolfi. There are no rankings or recommendations — just a structured look at how these distros differ so you can evaluate them in your own context.

Videos
card image
Mar 9, 2026
jOOQ Deep Dive: CTE, MULTISET, and SQL Pipelines

Some backend developers reach the point where the ORM stops being helpful. Complex joins, nested result graphs, or CTE pipelines quickly push frameworks like Hibernate to their limits. And when that happens, teams often end up writing fragile raw SQL strings or fighting performance issues like the classic N+1 query problem. In this video, we build a healthcare scheduling application NeonCare using jOOQ, Spring Boot 4, and PostgreSQL, and show how to write production-grade SQL directly in Java while keeping full compile-time type safety.