Investigating Memory Results > Finding Memory Leaks
The key to fixing and avoiding loitering objects is good reference management. Loitering objects are objects in the heap that are reachable through references from the root set, but have no future value to the program. They are caused by a live object inadvertently or unnecessarily retaining a reference to the loiterer for some period of time. To allow the garbage collector to remove a loitering object—and possibly further instances that it references—you must remove all references to the loiterer.
Some objects are reachable from the root set, while others are not. A root is an initial entry point to the Java heap; the root set includes all entry points to the heap for your program. There are several ways that objects can be part of the root set, including but not limited to being one the following:
• on the stack as a local variable
The objects connected to the root set are either live or loitering—you need to determine if the objects have future value to your program. The unconnected objects no longer have any references and may be ready for garbage collection.
The loiterers that are easiest to detect are those that are allocated to the heap but not removed after the program is finished with them; these loiterers remain for the duration of the program’s execution. More difficult to find are loiterers that are eventually freed or reused, but in the meantime consume memory that your program could be using for other tasks.
Four common causes of loitering objects are:
• Obsolete Collection References
• Lingering Transitional References
An obsolete collection reference occurs when an object is added to a collection, but not removed when no longer required. When methods add objects without removing them, the collection can grow without bounds over the runtime of the program. You can avoid creating loitering objects by pairing add/remove calls.
Obsolete listeners are a type of obsolete collection reference. However, they warrant their own category because they are a common cause of loitering objects. Obsolete listeners also have a greater impact on the performance of your program because, as your listener collections grow, your program has to propagate events to more and more listener objects.
Long-lived objects often contain one or more transitional references, that is, references that will be reassigned to new objects when the state of the long-lived object changes. There are two situations to consider.
In the first case, a transitional reference holds onto its object (lingers) until the method that sets the reference is called again. For example, consider a print service that has a reference to the current print job. The job prints, but when it is complete, the reference is not freed, so the print job remains in memory until the next time you want to print something. You can avoid creating these kinds of loitering objects by setting the transitional reference to null when the object is no longer required.
In the second case, a singleton object changes state, but one of its transitional references is not updated. For example, consider an object that maintains information about files in a directory and has references to the largest, smallest, and most complex files. When you change directories, for some reason only the references to the largest and smallest files are updated—the reference to the most complex file still points to the file in the previous directory. This is really a bug, but one that is hard to track down. If you approach the problem as a loitering object, you may find it more quickly. Maintenance of singleton objects can be made easier by encapsulating state transitions in a single method, rather than scattering changes of state throughout your code.
Long-running methods and stalled threads can pin objects in the heap until the method or thread returns. For example, consider the StalledStack.class application in the demos directory. The demo reads an XML file using DOM, parses out Java file names, and then performs a long-running (dummy) process on the parsed file names without freeing the memory taken up by the DOM tree. The memory will eventually be freed when the long-running process completes, but in the meantime, memory that could be used for the process is unavailable.