Eliminate obsolete object references

When you switch from a language with manual memory management, suc as C or C++, to a garbage-collected language, your job as a programmer is made much easier by the fact that your objects are automatically reclaimed when you’re through with them.

// Can you spot the "memory leak"?
public class Stack {
    private static final int DEFAULT_INITIAL_CAPACITY = 16;

    private T[] elements;
    private int size;

    @SuppressWarnings("unchecked")
    public Stack() {
        this.elements = (T[]) Array.newInstance(Object.class, DEFAULT_INITIAL_CAPACITY);
    }

    public void push(T element) {
        ensureCapacity();
        elements[size++] = element;
    }

    public T pop() {
        if (size == 0) {
            throw new EmptyStackException();
        }
        return elements[--size];
    }

    private void ensureCapacity() {
        if (elements.length == size) {
            elements = Arrays.copyOf(elements, 2 * size + 1);
        }
    }

}

There’s nothing obviously wrong with this program. You could test it exhaustively, and it would pass every test with flying colors.

public class StackTest {
    private final ExpectedException expectedException = ExpectedException.none();

    @Rule
    public ExpectedException getExpectedException() {
        return expectedException;
    }

    @Test
    public void testPushPop() {
        Stack stack = new Stack<>();
        stack.push(1);
        stack.push(2);
        stack.push(3);

        Assert.assertEquals(Integer.valueOf(3), stack.pop());
        Assert.assertEquals(Integer.valueOf(2), stack.pop());
        Assert.assertEquals(Integer.valueOf(1), stack.pop());

        expectedException.expect(EmptyStackException.class);
        stack.pop();
    }

So where is the memory leak? If a stack grows and then shrinks, the objects that were popped off the stack will not be garbage collected, even if the program using stack has no more references to them. This is because the stack maintains obsolete references of those objects.

The corrected version of pop() method looks like this:

public T pop() {
        if (size == 0) {
            throw new EmptyStackException();
        }
        T element = elements[--size];
        elements[size] = null;
        return element;
    }

Whenever a class manages its own memory, the programmer should be alert for memory leaks.

Another common source of memory leaks is caches – possible solutions for removing elements from cache are:

  • a separate background thread (perhaps a Timer or a ScheduledThreadPoolExecutor)
  • a side effect of adding new entries to the cache
  • LinkedHashMap class facilitates the latter approach with removeEldestEntry
  • sophisticated caches may use java.lang.ref directly

A third common source of memory leaks is listeners and other callbacks.

Lasă un răspuns

Adresa ta de email nu va fi publicată. Câmpurile obligatorii sunt marcate cu *

Acest site folosește Akismet pentru a reduce spamul. Află cum sunt procesate datele comentariilor tale.