post-images/eliminate-obsolete-object-references/cover.jpg

Eliminate Obsolete Object References

Today we will continue from where we left off and consider item 7 of Creating and Destroying Objects, the first unit of Effective Java.

I will enter our topic today on Garbage Collection. Java developers do not deal with memory management like other languages, this process is undertaken by garbage collection and it is really successful in this regard. But the direct perception of "Garbage collector collects everything, I don't need to think about memory" is not very realistic either. No matter how much it manages memory for us, it can cause memory leaks about not cleaning (not releasing) unused object references. We will focus on how we delete our objects from memory without a memory leak with you.

Of course, the best way is to go through the example, our example:

// Can you spot the "memory leak"?  
public class Stack {
    private Object[] elements;
    private int size = 0;
    private static final int DEFAULT_INITIAL_CAPACITY = 16;

    public Stack() {
        elements = new Object[DEFAULT_INITIAL_CAPACITY];
    }

    public void push(Object e) {
        ensureCapacity();
        elements[size++] = e;
    }

    public Object pop() {
        if (size == 0) {
            throw new EmptyStackException();
        }

        return elements[--size];
    }

    /**  
     * Ensure space for at least one more element, roughly * <p>  
     * doubling the capacity each time the array needs to grow.  
     */
    private void ensureCapacity() {
        if (elements.length == size) {
            elements = Arrays.copyOf(elements, 2 * size + 1);
        }
    }
}

When we look at our code from the point of view of the buyer, there is no problem. All our tests will be successful. That's one of the bad things about it. Although everything seems rosy, there is a memory leak in this code. When it comes to what a memory leak can cause, in extreme cases such memory leaks can cause disk paging (disk paging) or even crash your application with OutOfMemoryError, but such errors are relatively rare.

To interpret the stack structure in the code example above: When an object is increased in size and then reduced, the reduced amount should normally be removed from memory. Garbage collector does not do this, it cannot do it with a more correct discourse. Because even though you cannot access the (pop()) elements removed from the stack again, the stack keeps the references of these objects for you in the background. Even worse, it could happen in a chain reaction here. If there are other objects in the other objects added to the Stack, they are not deleted from the memory. The garbage collector does not delete objects in memory that point to a reference, thinking they are being used. So how can we make these objects that garbage collector can delete? In this kind of problem, assigning null for unused object references is the same as telling the garbage collector "I'm done with this object, you can clean it up".

public Object pop() {
    if (size == 0) {
        throw new EmptyStackException();
    }

    Object result = elements[--size];
    elements[size] = null; // Eliminate obsolete reference  

    return result;
}

Thanks to the null marking step shown in the example, the next trigger of the garbage collector will be able to delete these idle objects since no one references them. This null assignment mentioned is an exceptional case that we do not want to do. We do this because the array of elements in the Stack class in our example does its memory management. In this case, the part between 0 and the size value of the array is expressed as the active region. Since the size will change when the active portion in the stack is increased by additions and decreased by decrements, the references that are equal to or greater than the size value of the removed objects are addressed as the inactive portion. The garbage collector cannot capture these inactive zones, so we had to assign null for inactive zones. The best way to get rid of such unused references is the scope structure. It is necessary to exclude the variable containing the null reference. If you define each variable in the narrowest scope possible, unused variables will naturally be out of scope and will be caught and cleaned by the garbage collector.

In summary, you have to constantly consider the possibility of memory leaks in classes that manage their memory. When an item is released, all object references in the item must be deleted.

Finally, another common source of memory leaks is caching. Once you put an object reference in a cache, it's easy to forget it's there. It is difficult to detect that this cache is not used unless you do special research in the flow. By keeping the cache keys on a time basis on WeakHashMap or by any caching system (eg Memcache), you get the guarantee that the caches will be deleted in case they are not used.

JavaEffective JavaCreating and Destroying ObjectsObsolete Object ReferencesMemory LeaksGarbage CollectorOutOfMemoryError