Fully featured low overhead profiler for Java EE and Java SE platforms.
Monitoring and profiling solution for Gradle, Maven, Ant, JUnit and TestNG.
Easy to use performance and memory profiler for .NET framework.

Memory leaks

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 the memory telemetry, by watching how used memory grows. You can use Force Garbage Collection to immediately see whether some of the objects that consume the memory can be collected, thus decreasing used memory. If after 2-3 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 high memory usage 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.

Using the generations feature helps a lot in finding memory leaks, but you can also effectively find memory leaks analyzing a snapshot which does not contain object generation information, e.g. if you have not advanced generation when appropriate or if you have a HPROF snapshot. In this case use 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 the set.
    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.