Are you using a Java application that runs well at first, but becomes slower after a while? Or perhaps it works well for a small set of files, but performance slows down with more files? If you say “Yes” to any of these questions, then your java application might have a memory leak. Technically called “Java Memory Leaks.”
What are Memory Leaks in Java?
Memory Leaks in Java is a condition that occurs when no longer used objects are present in the application, and the garbage collector cannot remove them from memory and, thus, they remain there unnecessarily.
Moreover, memory leaks aren’t common, as they can block memory resources and reduce system performance over time. If not addressed on time, the application will consume more and more resources, resulting in a fatal OutOfMemoryError.
For a better understanding of the concept, here’s a simple visual representation for you
As we can see, here we have two types of objects – referenced and unreferenced; the garbage collector will periodically remove the unreferenced objects, whereas it never collects the objects that are still being referenced. This is where memory leaks can happen.
What Causes Memory Leaks?
A memory leak in Java can occur due to various reasons. Some of the most common causes for these memory leaks are:
1. Through Static Fields
One of the most potent causes of memory leaks in Java is the heavy use of static variables.
2. Through Unclosed Resources
Whenever you create a new connection or open a stream, the JVM allocates memory for these resources. In such cases, if you forget to close these resources can block the memory, reduce performance, and may even result in OutOfMemoryError.
3.Improper equals() and hashCode() Implementations
When creating new classes, the chances of memory leaks are pretty high when equals() and hashCode() methods are not properly overridden.
4. Through finalize() Methods
Another potential cause of memory leaks in java is the use of Finalizers. When the finalize() method does not work optimally, and can’t keep up with the Java garbage collector, then the application will encounter an OutOfMemoryError.
5. Using ThreadLocals
ThreadLocals allow us to isolate a state to a single thread, allowing us to achieve thread-safety. But when these ThreadLocals are not used properly can result in memory leaks.
How to Find Memory Leaks in Java Web Application?
Finding memory leaks in your Java application can be quite challenging if you are unfamiliar with the Java Virtual Machine (JVM) production environment.
However, there are some of the most common and effective ways by which you can detect a memory leak in Java.
1. Verbose Garbage Collection
Enabling verbose garbage collection is one of the fastest ways to detect memory leaks.
By adding the -verbose:gc parameter to the JVM configuration of the application, you enable the detailed GC trace. Summary reports are shown in the default error output file, so you can see how your memory is managed.
2. Do Profiling
The second technique to diagnose the memory leaks in Java applications is enabling Java profilers. The most popular profiler is Visual VM, which analyzes what works internally in your application — for instance, how memory is allocated.
3. Review Your Code
This is one of the best practices than a specific technique for dealing with memory leaks.
Simple practices such as thorough code reviews, and make good use of static analysis tools to help you better understand your code and the system you’re working on.
4. Use Reference Objects to Avoid Memory Leaks
To deal with memory leaks, you can also use Java reference objects, which come in the java.lang.ref package. Instead of directly referencing objects in java.lang.ref, you can use special references to objects that can be easily garbage collected.
You can measure and analyze the Java code’s performance by executing benchmarks. In this way, you can compare the performance of alternative approaches to accomplish the same task. This can help you choose a better approach and save memory.
How to Fix Memory Leaks in Java?
As of now, you have a better understanding of memory leaks, why they occur, and how to find them. Now, let’s look at how you can deal with these memory leaks.
There are two approaches by which you can fix these memory leaks.
1) Quick fix: Eclipse Memory Leak Warnings (catches some leaks)
2) Manually disable and enable parts of your code, and observe the memory usage of your JVM using tools like VisualVM.
1) Quick fix: Eclipse Memory Leak warning/errors.
The eclipse will throw warnings and errors for obvious leaks in code that is compliant with JDK 1.5+.
To be more precise, anything that implements closable (since 1.5) (e.g. output stream since 1.5) will issue a warning if its reference is destroyed but the object has not been closed.
However, leak detection is not always enabled in eclipse projects. It might be necessary to turn them on first. As shown, enable them in your project settings:
Now eclipse will outline memory leaks:
2. Manually disable & enable parts of your code and observe memory usage of your JVM using a JVM tool like VisualVM.
The next approach can be done using a popular tool called Visual VM. Here’s how you can manually find and fix memory leaks in java web applications using VisualVM:
Step 1: Download the tool. Run your Java application. Attach VisualVM to your application.
Step 2: Open the command prompt terminal and type in the following command to Start Visual VM;
Step 3: Right-click on Tomcat in the left-hand sidebar and select ‘Heap Dump’.
Step 4: Click on the ‘OQL Console’ button at the top of the Heap Dump navigation bar.
It opens a console where you can query the heap dump. But before that, we need to locate each instance of org.apache.catalina.loader.WebappClassLoader.
For this, Enter the following command in the resulting console.
select x from org.apache.catalina.loader.WebappClassLoader x
Step 5: In this case, VisualVM found two instances of the web application class loader; one for the web application itself and the other for the Tomcat manager application.
Use the Tomcat manager application to restart the web application and take another heap dump of the Tomcat process.
Step 6: From the above step, you will see an additional instance.
This is because one of these three instances was supposed to be collected by the garbage collector, but it wasn’t. By using Tomcat, we can easily identify which instances were not garbage collected, as all active class loaders are configured with the field name: ‘started’ set to ‘true.’
To find the invalid instance, click through all class loader instances until you find the one whose ‘started’ field is set to ‘false’.
Step 7: Identify what object is holding a reference to the class loader
Now that we have identified the class loader causing the memory leak, then we need to identify what object is holding a reference to the class loader.
Therefore, on the bottom pane of the instances tab, right-click on the object instances that form the root of the reference graph and select ‘Show nearest GC root’.
Step 8: Right-click on the instance and select ‘Show Instance’.
Step 9: See what is causing the memory leak.
From this, we can understand that this is an instance that is holding a reference to the WebappClassLoader. Hence, this is the errant reference causing the memory leak.
Step 10: Once you find what is causing the memory leaks, inspect the ‘Monitor’ and the ‘memory pools’ tab. If you see that your memory increases in the ‘Monitor’ tab, try pressing ‘Perform GC’ (garbage collection) and see if that decreases memory usage.
Step 11: Then switch over to the ‘memory pools’ tab and inspect the ‘Old Gen’.
Step 12: Go back and comment out most of the code in your program so the application just starts and stops.
Step 13: Repeat until the application is leak-free.
Step 14: Then, through several iterations, re-enable parts of your code and inspect VisualVM memory usage. When your application starts leaking again, go into the method that caused the leak and narrow it down.
Step 15: Eventually, you’ll narrow the problem down to a single class or method. As soon as you’re there, verify that all file buffers are closed and that Hashmaps are used properly.
How to Prevent Java Memory Leaks?
As we discussed above, what causes memory leaks in Java, here’s how you can prevent it.
- Reduce the use of static variables.
- To close resources, always use a finally block.
- Override equals() and hashCode() whenever creating new entities
- When an inner class does not need to access the members of its containing class, consider turning it into a static class.
- Always try to avoid finalizers.
- Clean up ThreadLocals whenever they’re no longer needed.
Memory leaks in Java applications are among the most difficult problems to resolve, as the symptoms are varied and difficult to reproduce.
One of the quickest ways to find and fix memory leaks is to use a Java Application Performance Monitoring (APM) solution like Seagence that allows you to instantly track, analyze, and manage memory issues in your Java application in real-time.
So, what are you waiting for? To know more about Seagence and to fix your errors easier than ever, click here!