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.
Should You Worry About Memory Leaks?
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:
- the size of the leak
- the program’s lifetime.
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.
How to Avoid Java Memory Leaks
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.
1. Use reference objects to avoid 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?
- SoftReference object: garbage collector is required to clear all SoftReference objects when memory runs low.
- WeakReference object: when the garbage collector senses a weakly referenced object, all references to it are cleared and ultimately taken out of memory.
- PhantomReference object: garbage collector is unable to clean up PhantomReference objects automatically, leaving you to manually clean up all PhantomReference objects and references.
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.
2. Avoid memory leaks related to a WebApp classloader
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.
- Static fields are started with the classloader’s value. Even as Jetty stops deploying and then redeploys your web application, the static reference persists, so the object cannot be cleared from memory.
- Daemon threads that are started outside the lifecycle of a web application and are prone to memory leaks because these threads have references to the classloader that started the threads.
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:
- AWT leak preventer
- DOM leak preventer
- Driver manager leak preventer
- GC thread leak preventer
- Java2D leak preventer
- LDAP leak preventer
- Login configuration leak preventer
- Security provider leak preventer
3. Other specific steps
BurnIgnorance.com also lists several ways to prevent memory leaks in Java, including:
- Release the session when it is no longer needed. Use the HttpSession.invalidate() to do this.
- Keep the time-out time low for each session.
- Store only the necessary data in your HttpSession.
- Avoid using string concatenation. Use StringBuffer’s append() method because the string is an unchangeable object while string concatenation creates many unnecessary objects. A large number of temporary objects will slow down performance.
- As much as possible, you should not create HttpSession on your jsp page. You can do this by using the page directive <%@page session=”false”%>.
- If you are writing a frequently executed query, use PreparedStatement object rather than using Statement object. Why? PreparedStatement is precompiled, while Statement is compiled every time your SQL statement is transmitted to the database.
- When using JDBC code, avoid using “*” when you write your query. Try to use the corresponding column name instead.
- If you are going to use stmt = con.prepareStatement(sql query) within a loop, then be sure to close it inside that particular loop.
- Be sure to close the Statement and ResultSet when you need to reuse these.
- Close the ResultSet, Connection, PreparedStatement and Statement in the final block.
What to Do When You Suspect Memory Leaks
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:
- Java heap space: memory resources could not be allocated for a particular object in the Java heap. This can mean several things, including a memory leak or lower specified heap size than the application needs or your program is using a lot of finalizers.
- PermGen space: the permanent generation area is already full. This area is where the method and class objects are stored. You can easily correct this by increasing the space via –XX:MaxPermSize.
- Requested array size exceeds VM limit: the program is trying to assign an array that is > than the heap size.
- Request <size> bytes for <reason>. Out of swap space?: an allocation using the local heap did not succeed, or the native heap is close to being used up.
- <Reason> <stack trace> (Native method): a native method was not allocated the required memory.
Less Common Memory Leaks
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.
A quick word about Java profilers
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.
Java Profiling Metrics
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:
- A method’s CPU time
- Memory utilization
- Information on method calls
- What objects are created
- What objects are removed from memory or garbage collected
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.
Other tools you should know
- NetBeans Profiler – supports Java SE, Java FX, EJB, mobile applications, and Web applications and could be used to monitor memory, threads and CPU resources.
- JProfiler – a thread, memory and CPU profiling tool that can also be used to analyze memory leaks and other performance bottlenecks.
- GC Viewer – an open-source tool that allows you to easily visualize information produced by JVM. You can use GC Viewer to see performance metrics related to garbage collection, including accumulated pauses, longest pauses and throughput. Aside from enabling you to run garbage collection, you can also use this tool to set up the preliminary heap size.
- VisualVM – based on the NetBeans platform, VisualVM is an easily extensible tool using various plugins to give you detailed data on your applications for monitoring both remote and local apps. You can get memory profiling and manually run the garbage collector using this tool.
- Patty in action – another open-source tool that you can use as a profiling tool to give you target and drilled down profiling. You can use this tool to analyze heaps.
- JRockit – a proprietary solution from Oracle, JRockit is for Java SE applications that may be used to predict latency, visualize garbage collection and sort through memory-related issues.
- GCeasy – GCeasy is a tool that analyzes logs related to garbage collection and is an easy way to detect memory leak problems when analyzing garbage collection logs. Another reason to use GCeasy is that it is available online; there is no need to install it on your machine to use it.
Java Memory Leaks: Solutions
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.
Using tools that can detect memory leaks
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.
Using heap dumps
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.
Using Eclipse memory leak warnings
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.
Additional Resources and Tutorials
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.
- Top .NET Developer Skills According to Tech Leaders and Experts - October 20, 2021
- What to Do About Java Memory Leaks: Tools, Fixes, and More - September 3, 2021
- What is Load Testing? How It Works, Tools, Tutorials, and More - February 5, 2021
- Americaneagle.com and ROC Commerce stay ahead with Retrace - September 25, 2020
- Stackify’s New Pricing: Everything you need to know - September 9, 2020