Slowness in Java Application Due To Increased FullGC Events: G1GC

In this blog, we will see one of the issues and solutions which I found in one of our production servers: our Java application became slow due to more GC pauses.  I will explain a particular approach that can be one of the reasons for initiating more GC pauses.

To understand this article, one must have basic knowledge of the G1GC algorithm of Java for garbage collection. Don’t worry if you don’t have knowledge of G1GC: I will write other articles on the basics of G1GC later, and then you can read this article again. 

Benefits of Setting Initial and Maximum Memory Size to the Same Value

When we launch applications, we specify the initial memory size and maximum memory size. For the applications that run on JVM (Java Virtual Machine), initial and maximum memory size is specified through "-Xms" and "-Xmx" arguments. If Java applications are running on containers, it’s specified through "-XX: InitialRAMPercentage" and "-XX: MaxRAMPercentage" arguments. Most enterprises set the initial memory size to a lower value than the maximum memory size. As opposed to this commonly accepted practice, setting the initial memory size the same as the maximum memory size has certain "cool" advantages. Let’s discuss them in this post.

1. Availability

Suppose you are launching your application with the initial heap size to be 2GB and the maximum heap size to be 24GB. This means when the application starts, the operating system will allocate 2GB of memory to your application. From that point, as the application starts to process new requests, additional memory will be allocated until a maximum of 24GB is reached. 

JVM Memory Architecture and GC Algorithm Basics

Purpose

This article discusses the basic concept of JDK8 and upwards memory management with heap and stack memory. and the basics of GC and its Algorithms.

Importance of Memory Management

Java garbage collector doesn't ensure that the heap memory will be completely free, and also, for a developer, it is not possible to force a garbage collector to run at a specific time. So it is helpful to know how memory management in Java works. 

Debugging RAM – Part 1: Java Garbage Collection – Java Heap Deep Dive

There are many excellent articles on Java Garbage Collection, Java Memory usage, and generally Java heap. Unfortunately, they are all over the place. They mix architecture, concepts, and problem solving as separate pieces. A lot of the material is out of date or doesn't include pragmatic information for solving problems with the garbage collector. E.g., pause times, heap space usage, etc.

In this post, I won't go into memory leaks. They're important but this is a different subject I would like to discuss in a post on its own. 

Apache Cassandra 4.0: Taming Tail Latencies with Java 16 ZGC

Like so many others in the Apache Cassandra community, I’m extremely excited to see that the 4.0 release is finally here. There are many, many improvements to Cassandra 4.0. One enhancement that is more important than it might look is the addition of support for Java versions 9 and up. This was not trivial, because Java 9 made changes to some internal APIs that the most performance-oriented Java projects like Cassandra relied on (you can read more about this here).

This is a big deal because with Cassandra 4.0, you not only get the direct improvements to performance added by the Apache Cassandra committers, you also unlock the ability to take advantage of seven years of improvements in the JVM (Java Virtual Machine) itself.

Chaos Engineering: Blocked Threads

In the series of chaos engineering articles, we have been learning to simulate various performance problems. In this post, let’s discuss how to make threads go into a BLOCKED state.

Sample Program

Here is a sample program from the open-source BuggyApp application, which would make threads go into a BLOCKED state. A thread will enter into a BLOCKED state when it couldn’t acquire a lock on an object because another thread already holds the lock on the same object and doesn’t release it. Review the program carefully.

Chaos Engineering – Stackoverflow Error

In our series of chaos engineering articles, we have been learning to simulate various performance problems. In this post, let’s discuss how to simulate a StackOverflow error. A StackOverflow error is a runtime error. In this post, we'll simulate a StackOverflowError, diagnose it, and solve the problem.

Sample Program

Here is a sample program from the open source BuggyApp application, which would generate java.lang.StackOverflowError.

Chaos Engineering: Deadlock

In this series of chaos engineering articles, we have been learning to simulate various performance problems. In this post, let’s discuss how to simulate deadlock.

What Is a Deadlock?

Deadlocks tend to happen in multi-threaded applications. The technical definition of a ‘deadlock’ is a situation where a set of processes are blocked because each process is holding a resource and waiting for another resource acquired by some other process. Here is a practical example that may help you understand deadlocks. 

Difference Between InitialRAMPercentage, MinRAMPercentage, MaxRAMPercentage

This article attempts to clarify the difference between InitialRAMPercentage, MinRAMPercentage, MaxRAMPercentage JVM arguments. These arguments have been introduced since Java 8 update 191. They are used to configure your Java application’s heap size when you are running it in the Physical server or in the container. In this article, let’s review their differences.

InitialRAMPercentage

‘-XX:InitialRAMPercentage’ is used to compute the initial heap size of your java application. Say suppose you are configuring -XX:InitialRAMPercentage=25 and overall physical memory (or container memory) is 1GB then your java application’s heap size will be ~250MB (i.e., 25% of 1GB).

The Best Way to Optimize Garbage Collection Is NOT By Tuning it

The best way to optimize garbage collection is NOT by tuning it.

When asked: "What would you do if your Java app experiences long GC pauses?" — most people would answer: "increase the heap size and/or tune the garbage collector." That works in many but not all situations. The heap may already be close to the total memory available. And GC tuning, beyond a few most obvious and efficient flags, often becomes a black art where each new change brings a smaller improvement. Worse, hyper-tuning GC makes it tightly coupled with your current heap size, number of CPUs, and workload patterns.

Need help choosing the right GC? Check out this post!

If any of these components changes in the future, the GC may suddenly perform much worse. And (what a surprise!) at that point, it may be really hard to remember why each of the flags in the combination like the one below is there, and what its effect was supposed to be: