Memory Leak is a decease and OutOfMemoryError (OOM) is the symptom for that. However all OOM doesn’t necessarily implies Memory Leak. OOM can happen due to the generation of large number of local variables – particularly with large number of concurrent requests (in the case of server applications). On the other hand all memory leaks not necessarily manifest into OOM – especially in the case of desktop applications or client applications (those are not run for long without restart).
Memory leaks also can be in heap, perm space, or in native memory.
Following section talks only about memory leak in heap. How to verify that memory leak is the reason for OOM in your case is also out of scope (probably will discuss some other day). How to attack memory leaks in perm space is also out of scope (again another day’s work). I have absolutely no clue about debugging memory leak in native memory (Google search with error messages).
Memory leak in heap
1) Run the application with latest available JDK (JDK 1.6 as of now). New versions of java contain lot of improvements in debugging tools. Run the application with different operations repeatedly to identify the operation that causes memory leak. We don’t need to wait until OOM to identify memory leak. Monitoring the heap using any tool can identify memory leak. Run the application until it reaches a steady state. Monitor the heap size over a period of time. If the ‘heap size after full GC’ (full GC remember) increases each time, that implies a memory leak. We can use visualVM also for this purpose.
Now we have identified the operation that causes memory leak – means if we invoke this operation repeatedly we will get OOM. (What if we could not identify the operation that causes memory leak?). Now onwards we assume that this operation runs continuously.
2) Install VisualVM (https://visualvm.dev.java.net/download.html). You need to install VisualVM separately – unlike many other java debugging tools this one doesn’t come along with JDK [ Thanks to Jiri Sedlacek's comment! VisualVM is part of JDK from jdk1.6 update 7 onwards ]. Just unzip the file to any convenient location.
3) Run visualVM - you can run visualvm by executing ‘visualvm.exe in the bin directory. (You should have jdk 1.6 in path?)
4) Connect to the application – all the java processes running in the machine are listed in the left side pane (name of the main class shall be listed). You can double click the one you are interested. Right side there are four tabs – monitor and profiler are the ones we are interested in.
5) Go to the profiler tab. Check the settings checkbox it will display different options. Go to the memory settings tab. Check the ‘record allocations stack traces’. Uncheck the ‘settings’ checkbox; that will close the settings options display (all changes are automatically saved).
6) Now click on the ‘memory’ button – this will start memory profiling. Wait for few seconds – it will take a while. A tabular display of different classes, its instance count, total byte size etc are displayed.
7) Wait for a while to allow the application to be in stead state.
8) Take a snapshot of the objects by clicking the button on top of the tabular view (see below screen shot). A snapshot is generated and labeled in the left side pane as well as opened in the right side pane.
9) Wait for some more time to allow some memory leak. Take one more snapshot as explained in the previous step. Now we have two snapshots displayed at the left side pane (also opened in the right side pane).
10) Select both of these snapshots (in the left side pane, by using ‘ctrl’ button we can select multiple items), right click and select ‘compare’. A comparison tab shall be opened in the right side pane. That tab will display items that have been increased during the interval of first and second snapshot. The top item is the suspect of memory leak.
11) Go to the profiler tab again. Select the identified item in the previous step. Right click and select ‘Take snapshot and show allocation stack traces’. One more snapshot is generated. This time in the right side pane, an allocation stack traces sub tab also visible. It lists different places where this particular item is instantiated and it’s percentage of total count.
12) Wait for some more time to have more memory leak. Take one more snapshot with allocation stack traces.
13) Take a heap dump of the application. We can take heap dump by right clicking the application in the left side pane and select ‘heap dump’. Heap dump is also displayed in the left side pane as well as opened in the right side pane.
13) Now come back to the tow snapshots with stack traces. Compare these two snapshots with allocation stack traces. Identify the methods where there is large difference in contribution to total count. These are the places where leaked objects are instantiated.
14) Go to the heap dump tab in the right side pane. Go to the Classes view. Double click on the item identified in step 10. Instances of that class are displayed. Choose one instance which you feel could one of the leaked objects (just intuition). Look at the right bottom pane. Expand the view. You will find the objects that is holding referenced to the leaked objects. This is the reason for memory leak.
15) From the items identified in step 10, and clue we got from step 13 and 14 we could resolve the memory leak issues. We get the object which leaked, the place of instantiation, and the objects that hold reference to it.