What is a memory leak?
Memory leak is an existence of objects that are not needed anymore according to the application logic,
but still retain memory and cannot be collected because they are referenced from other live objects,
due to a bug in application itself.
Obviously, each leaked object is accessible from at least one GC root or represents a GC root. In other words, for each leaked object there is always a path that starts from GC root(s) and contains (ends with) the leaked object.
How to find memory leaks?
1. Detect memory leak, i.e. understand what objects do exist while they should not.
If you do not know yet what objects are leaked, YourKit Java Profiler can help you find out.
You can suspect the presence of memory leaks with the help of Telemetry, by watching how used memory grows in the "Memory" tab. You can use "Force Garbage Collection" to see whether some of the objects that consume the memory can be collected, thus decreasing used memory. If after those explicit garbage collections the used memory remains on the same level or decreases insignificantly, this possibly means you have a leak. ("Possibly," because it can turn out not to be a leak, but simply high memory consumption -- Learn how to deal with that case)
Also consider the capture snapshot on low memory feature. Snapshots captured automatically using this feature may possibly be captured in the state when memory usage reaches the specified threshold because of memory leaks. Thus, the snapshots will provide information sufficient to discover and fix the leaks.
To detect a memory leak, use the following scenario:
- Connect to profiled application
- Advance generation number just before executing the task which is suspected to cause leak.
- Let the application perform the task.
- Capture memory snapshot and open it.
- Use the "Generations" view to see objects created during the task execution; leaked objects should be among them.
You can also analyze a single snapshot, with the help of Statistics section in the "All Objects" view.
Often, potential leaks in particular profiled application are well known in advance, i.e. you know a class of objects that have a "tendency" to
leak while application is being developed, changed, refactored.
You can easily check for the presence of such objects, with the help of
Memory | Instances by Class... (Ctrl+N).
In more complex cases, you can use Set description language to declare sets of potentially leaked objects.
Note that even one class can have objects legally and illegally (leaked objects) retained in memory.
To distinguish between them, you can use objects explorer to see outgoing and/or incoming references. For example,
an object may have a
String field with the value that can identify this object among other objects of the same type.
2. Once you have found leaked object(s), it's time to understand why they are retained in memory
Select the leaked object(s) in a memory view, and use
Memory | Paths from GC Roots... (Ctrl+P).
See Working with paths for details.
Let us look at a simple example of how to use paths.
Assume that we are profiling an Editor application that can open, edit and close text files. There is a singleton object that acts as a manager of opened files, and the data of each opened file is represented with an instance of class Document.
During the profiling session we open several text files, edit them, close them and take a memory snapshot.
If everything is correct, there should be no instances of Document that cannot be collected.
So, first of all, we use
Memory | Instances by Class... (Ctrl+N), to see if there are leaked Documents.
Assume we have found such objects - so we do have a leak. There should be paths from GC roots to these objects (or, perhaps, some of them may belong to GC roots themselves - this will be indicated in the view).
Thus we search for the paths from GC roots to one of the Documents (Ctrl+P), or to all Documents defining
If all the paths go through the manager singleton, the code responsible for closing files in our editor must have a bug.
If none of the paths contains the manager singleton, then the closing operation works correctly, but there are object(s) in the path(s) that erroneously hold references to Documents and cause the memory leak.
Browsing paths, use navigation feature of IDE integration.