Friday, March 17, 2006

Java Memory leaks

Memory leaks were and are a serious concern for Java programmers. Though garbage collection helps avoid many causes of memory leaks in other languages (like unfreed pointers of C/C++), Java programs have some sources of memory leaks. The root cause of memory leaks in Java is any program that holds a reference to an object that will not be used anymore. Although such memory leaks are undetected in small programs, they will be more visible in applications that run on a server for a long/indefinite time. Another case is when native code (written in C/C++) invoked from Java (through the Java Native Interface) does not release system resources. This is because the the Java garbage collector does not collect the memory held by C pointers (Conservative garbage collection). It is hard to identify if your Java application has memory leaks by simply looking at the code as they show up only in runtime. The hint of a memory leak is the java.lang.OutOfMemoryError (This does not necessarily mean a memory leak, you may simple need more memory to run your application). I tried to list out a few causes of memory leaks in Java applications and some recommendations based on my experience and with the help of a few articles that I listed at the bottom:

ResultSet and Statement Objects
JDBC ResultSet and Statement objects will be closed when the connection that created them is closed. However, while using pooled connections, if close() is invoked on the connection, the connection will be returned to the connection pool rather than being closed. Consequently, the ResultSet and Statement objects created by the connection will not be closed automatically, and are a potential cause of memory leaks.

Solution
Explicitly close all ResultSet and Statement objects created using pooled connections. It is also a good practice to explicitly close ResultSet and Statement objects as this will release any resources held by these objects.

Collection Objects
A Java collection holds references to other objects. It grows as elements are added to it. While the collection itself has a longer lifetime, individual elements within it do not. Hence, if the individual collection elements are not removed, the collection can grow indefinitely, and the JVM could run out of memory. Also known as Lapsed listeners since they are most commonly observed in event listeners held in collections.

Solution
Explicitly remove objects from collections if they will not be used anymore. Weak references may also solve the problem in some cases. Using weak references to plug Java memory leaks is discussed in : Java theory and practice: Plugging memory leaks with weak references. An example of using the WeakHashMap is also provided in the article.

Static classes and singletons
Static variables and classes, once loaded, will exist through out the lifetime of the application. The same is the case with Singleton objects. If there are too many singletons/static classes in the application, the available memory in the JVM will be significantly decreased for the rest of the application's lifetime. In this case it is the classloader that becomes too large. The proliferation of singletons antipattern addresses this specific issue. A similar problem is incorrect scoping. If you have an variable that might only be needed within a single method as a member variable of a class, then the variable effectively has the same lifetime as the class.

Solution
Avoid creating too many singletons. Too many is a relative term and is dependent on your application requirements and memory availability.

HttpSession
Data stored in the HttpSession will be available till the user logs out. Storing too much data in the HttpSession will quickly lead to OutOfMemoryError. Another problem is when persistent session are used, then the servlet may need to serialize/deserialize the objects stored in the session and this adds a significant overhead in case of large objects.

Solution
Use HttpRequest to transfer data whenever possible instead of HttpSession. If the objects have to be available for longer time, then they can be moved to the business layer. Elements in the session must be explicitly removed Java memory leaks -- Catch me if you can

Caching
Caching can play a significant part in Java memory leaks. It is similar to HttpSession usage, but outside the scope the Web tier. Too little caching will not give the expected performance boost, while too much caching may hamper performance by taking up a lot of memory.

Solution
There are a lot of sophisticated caching frameworks available (open source and commercial) which handle the memory efficiently. These frameworks come to use if you have an application that is heavily dependent on caching. A cheaper way of caching would be to use soft references, they are more suited for simpler caching requirements.

To summarize, the following is quick list of the sources of memory leaks in Java applications:
  • ResultSet and Statement Objects
  • Static classes and singletons
  • Incorrect Scoping
  • HttpSession
  • Caching
References:
How Do You Plug Java Memory Leaks?
Plug memory leaks in enterprise Java applications
Java theory and practice: Plugging memory leaks with soft references

1 comment:

  1. That's very good summary thanks.

    Does using valueOf(something) instead of .parseInteger(something) is a leak or temp leak too?

    ReplyDelete