Spring Boot Performance – Analysing Abnormal Process Behavior: Memory Leaks

Estafet consultants occasionally produce short, practical tech notes designed to help the broader software development community and colleagues. 

If you would like to have a more detailed discussion about any of these areas and/or how Estafet can help your organisation and teams with best practices in improving your SDLC, you are very welcome to contact us at enquiries@estafet.com 

What Is a Memory Leak

A memory leak in software is a condition where a program consistently fails to release memory that is no longer needed, leading to a gradual fill-up of available memory resources. This issue is critical in the context of Spring Boot applications, which operate within a Java Virtual Machine (JVM) environment. Spring Boot’s convention-over-configuration principle simplifies application development, but it does not inherently safeguard against the improper management of memory. 

Memory leaks in Spring Boot can manifest through several scenarios, such as unclosed resources, long-lived collections growing unbounded, or static references preventing garbage collection. Detecting and addressing these leaks is essential for maintaining optimal application performance and preventing the exhaustion of JVM heap space, which can lead to OutOfMemoryError exceptions, application downtime, and costly resource usage. As such, understanding the mechanics of memory management within the JVM and employing tools and practices to monitor and analyse memory usage are crucial steps in identifying and rectifying memory leaks in Spring Boot applications.

The most common reason for memory leaks is heavy class static field usage. The reason for that is that static variables usually live throughout the entire uptime of the application, and if the memory around them is handled improperly, a memory leak is imminent.

Early Symptoms of a Memory Leak

The most obvious one – OutOfMemoryError error thrown/logged from the application. Gradual increase in memory consumption over runtime. The application crashes, lags, and loses connection, an overall performance degradation.

Is It “Really” a Memory Leak?

Surprisingly “OutOfMemoryError” does not always mean that you have a memory leak. There are cases where you unintentionally reserved too little memory for the heap. You may have heard of the two JVM parameters “-Xms2048m -Xmx4096m”. Have a look at this great article on the topic.

Example of a Memory Leak

Consider the following sample Spring Boot Application:

The application has 2 endpoints, one that adds values to a cache and one that manually clears the cache. Maybe we manually clear the cache at intervals, maybe another microservice calls that endpoint in fixed intervals, doesn’t matter. What matters is that there is no control over the maximum allowed cache size. 

Let’s say we added a few hundred chunks of cache:

100 * 10 MB = 1G of cache, thats not too much, but notice how nothing is stopping us from adding more and more. Let’s do that:

And we get a crash:

To be exact. Ok, this was an obvious and telling case, but imagine that we added like 2GB of cache and it stayed there for days without it being needed after the first hour. This and similar problems arise more often than you might think. 

Ways to Prevent Memory Leaks

There are a few ways to reduce the risk of memory leaks with the code you produce.

Use a modern IDE

This. Using a modern IDE like IntelliJ, allows you to capture the more obvious issues by simply looking at the code analyzer that goes with it. For example, if you create a resource and forget to close it, IntelliJ has your back:

Which gets us to the next part:

Use try-with-resources

When working with any resources that implement AutoCloseable interface, it’s essential to put that resource in a try-with-resources block, which will call the closing logic upon leaving the block scope.

The writer from the previous example is now closed after we exit the scope and as you can see we no longer have the yellow highlight by the IDE.

Analyse Application Logs

Even If you don’t get consistent app restarts and crashes, it’s a good thing to have either a software or someone to manually check logs after long uptimes of the app. If you see even once an OutOfMemoryError – that’s bad, investigate and make sure the application has enough space reserved for it. If it does, you must try to reproduce the issue locally if it isn’t easily identifiable by the logs.

If you are able to reproduce the exception locally, that’s very good, because IntelliJ provides us with an amazing tool called a Profiler. It’s right there in the corner:

When you launch an app with the profiler enabled, you can see a real-time graphical representation of CPU and Memory resources being utilized. 

Remember our previous example program with a memory leak? This is what it looks like when we add to the cache:

See the drop at the end? That’s a call to:

This frees up the cache and suggests to the JVM that the garbage collector should be invoked.

This amazing tool can be used to create a controlled environment in which you can observe live how the memory is being managed and identify potential places of abnormal allocations.

Defensive Programming

Another thing you can consider is adopting “Defensive Programming” techniques. This style of writing code can greatly reduce the risk of memory leaks and other general bug cases.

For example, in the example we had with the caching from before – we could’ve avoided a memory leak if we implemented a check for the cache size before insertion, and either aborted the insertion or dumped the oldest element(just an example). 

Static Analysis Tools

Sometimes, as powerful as the IDE may be, it will miss a more niche bug we introduce. That’s where static analysis tools come into play. They can either be integrated into your pipelines, or the developers can trigger them manually. An example of a tool with such capabilities, empowered by AI, is Diffblue. We’ve covered it before with our AI unit testing article. You can find other high-quality static analysis tools (some even integrate with the IDE) here.

Conclusion

In this short article, we covered what a memory leak is, how to spot it, and how to try to prevent it.

By Antonio Lyubchev, Consultant at Estafet

Stay Informed with Our Newsletter!

Get the latest news, exclusive articles, and updates delivered to your inbox.