First realize how to get memory leaks:
A memory leak is the behavior of an application to consume constantly more and more memory. As all machines have an limited amount of RAM you application will crash with an OutOfMemoryError. Normally the garbage collector will search for dead object and release their memory. But when all objects are referenced (directly or indirectly through other objects) by a root set (e.g. static variable, or reference from the stack) no memory will be released.
The easiest way in Java to create such a leak is to have a list and a loop where you add values to the list but you have no place in your code where to take values out of the list. The loop even may be code that is regularly called by the user. A typical candidate for this is adding a event listener to a frame on a button click but the frame is never garbaged collected.
The tools you could use:
The jdk includes some tools you could use to profile your application for memory leaks.
- jps
Lists the id and the name of all running Java programs. The id is needed by other tools. - jconsole
A graphical tool that shows profiling information about a Java application. For searching memory leaks the "memory" tab is where to start. - jmap
Creates a dump of the heap.
- jhat
Analyses a dump created by jmap. Starts a webserver which you could reach via http://localhost:7000
Lessons learned:
- Use jconsole to find the part in your application that causes memory leaks. To do this, start your application and the jconsole. Then use a part of your application. The memory usage will increase. After a while, press the "Perform GC" button in jconsole several times. This is necessary as the garbage collector will not try to minimize the amount of memory but only ensures that there is enough. Repeat this until you find a situation where the memory increases and is not freed by the garbage collector. Try to minimize the steps you've done to cause the leak. Then analyse the code that is executed.
- jhat has the option baseline. The description implies that you can take a heap dump at time t1 and then a second heap dump at time t2 and then check with the help of baseline which objects are new. This didn't work for me. Almost all objects were new. Even the one that should have stayed the same (e.g. the main window) .
- jhat has the possibility to count the instances of a class and the count the memory it uses. Unfortunately it will break down the memory usage to the fields of an object. Say you have a large objects with a lot of fields and in sum the object with all its fields will use megabytes of memory the jhat will show only a few bytes for this object. The field instances will get the credit for the memory. This may be formally correct, but you want to get rid of the large object to get back your memory. So analyse your code for large objects and do a sanity check if the amount of instances may be correct or if it is to high.
- jhat offers the possibility to find all Rootsets for an instance. In my case it showed a lot of Rootsets for instances but when taking a closer look it turned out that all Rootsets where going through the same reference at one point or the the other. So the end of the stack traces where the same. This behavior is highly application dependent. In your case there may be really several references to the object. But check first if it is really that bad.
- Keep the heap size low when you want to find Rootsets for
objects. While staying below 20 megabyte the performance of jhat
was OK but when going above 30 megabyte it was pretty slow on my
machine. Well, my machine is a little bit old but on colleagues
computer it was also slow when the heap got large and they have faster
computers.