Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Automated memory leak detection in Java

I was thinking about automated memory leak detection for a Java program. The basic algorithm is to create JUnits that contain the following logic:

Call System.gc() several times
Determine initial heap memory consumption using either Runtime class or JMX
Loop 
    Do something that exercises program under test
End loop

Call System.gc() several times
Determine final heap memory consumption
Compare initial and final memory numbers

The loop is being used to see if memory is creeping up in small increments.

It is necessary to distinguish between expected and unexpected increases in memory use.

This is not really a unit test. But the JUnit framework is convenient to use.

Do you think that this approach is valid? Do you think that this approach will be sucessful in identifying memory leaks? Have you ever done something like this?

like image 559
David Avatar asked Jul 19 '11 15:07

David


People also ask

Can you have memory leaks in Java?

A small Java application might have a memory leak, but it will not matter if the JVM has enough memory to run your program. However, if your Java application runs constantly, then memory leaks will be a problem. This is because a continuously running program will eventually run out of memory resources.

What is used for avoiding memory leakage in Java?

Using java. lang. repackage allows you to avoid direct referencing objects and use special reference objects that the Garbage Collector can quickly clear. Let's see how Garbage Collector acts upon different reference objects to avoid memory leaking in java.


1 Answers

I developed a simple unit test framework for memory leaks which has worked reliably for me. The basic idea is to create a weak reference to an object which should be garbage collected, execute the test, perform a full GC, and then verify that the weak reference has been cleared.

Here is a fairly typical regression test using my framework:

public void testDS00032554() throws Exception {
  Project testProject = getTestProject();
  MemoryLeakVerifier verifier = new MemoryLeakVerifier(new RuntimeTestAction(getTestClassMap()));
  testProject.close();
  verifier.assertGarbageCollected("RuntimeTestAction should be garbage collected when project closed");
}

There are some things to note here:

  1. It is critical that the object you wish to have be collected should not be stored in a variable in your unit test as it will be retained through the end of your test.
  2. This is a useful technique for regression tests where a leak has been reported and you know which object should have been removed.
  3. One problem with this approach is that it is hard to determine why the test failed. At this point you will need a memory profiler (I am partial to YourKit). However IMO it is still useful to have the regression tests so that the leaks cannot be accidentally reintroduced in the future.
  4. I ran into some threading problems with not all references being cleared immediately, so the method now tries performing the GC a number of times before failing (as described in this article: Java Tip 130: Do you know your data size?)

Here's the full helper class in case you want to try it out:

/**
 * A simple utility class that can verify that an object has been successfully garbage collected.
 */
public class MemoryLeakVerifier {
private static final int MAX_GC_ITERATIONS = 50;
private static final int GC_SLEEP_TIME     = 100;

private final WeakReference reference;

public MemoryLeakVerifier(Object object) {
    this.reference = new WeakReference(object);
}

public Object getObject() {
    return reference.get();
}

/**
 * Attempts to perform a full garbage collection so that all weak references will be removed. Usually only
 * a single GC is required, but there have been situations where some unused memory is not cleared up on the
 * first pass. This method performs a full garbage collection and then validates that the weak reference
 * now has been cleared. If it hasn't then the thread will sleep for 50 milliseconds and then retry up to
 * 10 more times. If after this the object still has not been collected then the assertion will fail.
 *
 * Based upon the method described in: http://www.javaworld.com/javaworld/javatips/jw-javatip130.html
 */
public void assertGarbageCollected(String name) {
    Runtime runtime = Runtime.getRuntime();
    for (int i = 0; i < MAX_GC_ITERATIONS; i++) {
        runtime.runFinalization();
        runtime.gc();
        if (getObject() == null)
            break;

        // Pause for a while and then go back around the loop to try again...
        try {
            EventQueue.invokeAndWait(Procedure.NoOp); // Wait for the AWT event queue to have completed processing
            Thread.sleep(GC_SLEEP_TIME);
        } catch (InterruptedException e) {
            // Ignore any interrupts and just try again...
        } catch (InvocationTargetException e) {
            // Ignore any interrupts and just try again...
        }
    }
    PanteroTestCase.assertNull(name + ": object should not exist after " + MAX_GC_ITERATIONS + " collections", getObject());
}

}

like image 152
Andy Armstrong Avatar answered Sep 17 '22 14:09

Andy Armstrong