Memory management is Java’s strongest suit and one of the many reasons developers choose Java over other platforms and programming languages. On paper, you create objects, and Java deploys its garbage collector to allocate and free up memory. But that’s not to say Java is flawless. As a matter of fact, memory leaks happen and they happen a lot in Java applications.
We put together this guide to arm you with the know-how to detect, avoid and fix memory leaks in Java.
Memory leaks often involve small amounts of memory resources, which you might not expect to have problems with. But when your applications return a java.lang.OutOfMemoryError, then your first and most likely suspect will be a memory leak.
Memory leaks are often an indicator of poorly written programs. If you are the type of programmer who wants everything to be perfect, you should investigate every memory leak you encounter. As a Java programmer, there is no way to know when a Java virtual machine will run the garbage collector. This is true, even if you specify System.gc(). The garbage collector will probably run when memory runs low or when the available memory is less than what your program needs. If the garbage collector does not free up enough memory resources, your program will take memory from your operating system.
A Java memory leak is not always serious compared to memory leaks that happen in C++ and other programming languages. According to Jim Patrick of IBM developerWorks, there are two factors you should be concerned with considering a memory leak:
A small Java application might have a memory leak, but it will not matter if the JVM has enough memory to run your program. However, if your Java application runs constantly, then memory leaks will be a problem. This is because a continuously running program will eventually run out of memory resources.
Another area where memory leaks might be a problem is when the program calls for a lot of temporary objects that use up large amounts of memory. When these memory-hogging objects are not de-referenced, the program will soon have less available memory than needed.
To avoid memory leaks, you need to pay attention to how you write your code. Here are specific methods to help you stamp out memory leaks.
Raimond Reichert at JavaWorld writes that you can use reference objects to get rid of memory leaks.
Using the java.lang.ref package, you can work with the garbage collector in your program. This allows you to avoid directly referencing objects and use special reference objects that the garbage collector easily clears. The special subclasses allow you to refer to objects indirectly. For instance, Reference has three subclasses: PhantomReference, SoftReference and WeakReference.
A referent, or an object referenced by these subclasses, can be accessed using that reference object’s get method. The advantage of using this method is that you can clear a reference easily by setting it to null and that the reference is pretty much immutable. How does garbage collector act with each type of referent?
Using reference objects, you can work with the garbage collector to automate the task of removing listeners that are weakly reachable. WeakReference objects, especially with a cleanup thread, can help you avoid memory errors.
Using Jetty 7.6.6. or higher, you can prevent WebApp classloader pinning. When your code keeps referring to a WebApp classloader, memory leaks can easily happen. There are two types of leaks in this case: daemon threads and static fields.
With Jetty, you can use preventers to help you address problems associated with WebApp classloaders. For instance, an app context leak preventer, such as appcontext.getappcontext(), helps you keep the static references within the context classloader. Other preventers you can use include the following:
BurnIgnorance also lists several ways to prevent memory leaks in Java, including:
If you find it takes longer to execute your application or notice a considerable slowdown, it is time to check for memory leaks.
How do you know your program has a memory leak? A prevalent sign is the java.lang.OutOfMemoryError error. This error has several detailed messages that would allow you to determine if there is a memory leak or not:
There are times when your application crashes without returning an OutOfMemoryError message, making it more challenging to diagnose memory leaks as the problem and make corrections. The good news is that you can check the fatal log error or the crash dump to see what went wrong.
Moreover, there are many monitoring and diagnostic tools you can use to help identify and correct memory leaks. Stackify’s Darin Howard has identified Java profilers as an excellent way to track down memory leaks and run the garbage collector manually. You can use Java profilers to review how memory is being used, which will easily show you the processes and classes that are using too much memory. You can also use JVM Performance Metrics, which give you tons of data on garbage collection, thread counts and memory usage.
Java profiling helps you monitor different JVM parameters, including object creation, thread execution, method execution and yes, garbage collection.
When you have ruled out memory leaks as the reason for your application’s slow down, use Java profiling tools to get a closer view of how your application is utilizing memory and other resources. Instead of going over your code to find the problems, simply use these tools, which will save you the time and effort needed to ensure that your code is up to par.
Java profilers give you a comprehensive set of statistics and other information you can use to trace your coding mistakes. Profilers also help you find what is causing performance slowdowns, multi-threading problems and memory leaks. In short, profilers give you a more stable and scalable application. And the best part is these Java profiling tools will give you a fine-grained analysis of every problem and how to solve them.
If you use these tools early into your project and regularly – particularly when used in conjunction with other Java performance tools – you can create efficient, high-performing, fast and stable applications. Profiling tools will also help you know critical issues before you deploy your app.
Some metrics you can find out using Java profiling tools include:
The Java profiler Memory Analyzer (MAT) allows you to analyze Java heap to search for memory look and lower memory use. You can easily analyze heap dumps even when there are millions of objects living in them, see the sizes of each object and why garbage collector is not deleting specific objects from memory. MAT gives you a nifty report on these objects, helping you narrow down suspected memory leaks.
The Java Flight Recorder is a diagnostic and profiling tool that gives you more information about a running application and often better data than those provided by other tools. The Java Flight Recorder allows APIs created by third-party services and lowers your total cost of ownership. A commercial feature of Oracle Java SE, Java Flight Recorder also gives you an easy way to detect memory leaks, find the classes responsible for these leaks and locate the leak to correct it.
Now that you know your program has memory leaks, you can use these tools to help fix leaks when they become a problem – preferably before leaks become an issue.
For our next example, we are going to use VisualVM.
Once you have downloaded and configured VisualVM, analyze your code by running your application with VisualVM attached to it. When the task that slows down your application is performed, VisualVM looks at the “monitor” and “memory pools” tabs. What do you need to look out for? When you see spikes in memory usage in the Monitor tab, press on the “Perform GC” button, which will activate garbage collection. This should help decrease the amount of memory used.
If that does not work, switch to “memory pools” and look at the Old Gen section. If objects are leaking, you would see it here. Remember that active objects are placed in “Eden” and will then be moved to “Survivor.” Meanwhile, older objects are found in the ‘Old Gen’ pool.
At this point, you can go back to your code and comment out the irrelevant parts, up to the point where you notice that there is performance slow down or where it just stops. Repeat all these steps until you have eliminated all the leaks.
Enable some parts of your code to check memory usage, and if you find another leak, get into the method that caused these leaks to help plug it. Keep on narrowing it down until you only have a single class or method left. Validate all file buffers to see if these are closed. Also, check all hashmaps to see if you are using these properly.
If you find the above-mentioned method too tedious, you might be able to reduce the time you spend on fixing memory leaks by using heap dumps. Heap dumps allow you to see the number of instances open and how much space these instances take up. If there is a specific instance that you want to investigate further, you can just double click on that particular instance and see more information. Heap dumps help you know just how many objects are generated by your application.
Another way to save time is to rely on Eclipse memory leak warnings. If you have a code compliant with JDK 1.5 or higher, you can use Eclipse to warn you when a reference is ended but the object persists and is not closed. Just be sure to enable leak detection in your project settings. Be aware that using Eclipse might not be a comprehensive solution. Eclipse does not detect all leaks and may miss some file closures, especially when you have code that is not JDK 1.5 (or higher) compliant. Another reason why Eclipse does not always work is because these file closures and openings are nested very deeply.
Get more insights and information on how to avoid, detect and rectify memory leaks from the following resources and tutorials:
Memory leaks are certainly a concern for Java developers, but they’re not always the end of the world. Arm yourself with the know-how to prevent them before they occur and address them when they arise.
Stackify by Netreo is rapidly growing and so is the usage of our services. If your app is constantly changing or usage is increasing, it is critical that you have good tools in place for monitoring and finding the root cause of performance problems. Building better code can be easy in Java – Prefix provides an instant feedback loop to give you visibility into how your app is performing as you write code, and Retrace offers powerful application performance management (APM) for all your Java applications.
If you would like to be a guest contributor to the Stackify blog please reach out to [email protected]