Memory Leaks in Java – Part 2

Memory leaks in Java - stylized image of a man at a huge dripping tap with a bucket to one side

Last post we introduced the concept of memory leaks, what causes them and how to find and fix them. This post we’ll examine some code that can cause memory leaks.

Causes of Memory Leaks

Memory leaks can be caused by many different coding issues. These include the following:

  • The use of large static fields.
  • Unclosed file resources.
  • Incorrect or missing equals() and hashCode() implementations.
  • The creation of very large objects.
  • The use of anonymous classes.
  • The use of finalizers.
  • Insertion into Collection objects without deletion.
  • Unbounded caches.
  • Interned strings.
  • Lots of large session objects in a web application.
  • Listener methods that aren’t deregistered.

What Does The Static Modifier Do?

When we declare a field to be static, it means that the field belongs to the class itself, rather than to an instance of the class. This means that there is only one copy of the field in memory, regardless of how many instances of the class exist. In the same way, a static method belongs to the class itself. A static method cannot refer to any instance fields.

We must be aware of the effects in our code when using the static modifier.

Code that uses a lot of static variables and methods can be harder to test. This can introduce hidden dependencies between different parts of the program. It becomes more difficult to determine how changes to one part of our code might affect other parts. This can lead to inflexible code that is more difficult to change.

Static variables can lead to concurrency problems if multiple threads access and modify the same variable at the same time.

If a static variable is not properly released when it is no longer needed, it can lead to memory leaks and other performance issues.

Static Variable Examples

Static variables in Java can lead to memory leaks. They have a lifetime that is usually the same as the lifetime of the running application. Unless we explicitly set them to null, they live as long as the program runs. Forgetting to do this can cause memory leaks.

Take a look at the following code:

import java.util.*;

public class SomeClass {
    private static List<Integer> list = new ArrayList<>();

    public static void fillList(int number) {
        Random r = new Random();
        for (int i=0; i<number; ++i) {
            list.add(r.nextInt());
        }
    }
} // end of class

We can call the fillList() method as follows:

SomeClass.fillList(2_000_000);

The static list field belongs to the SomeClass class instance. After the fillList() method is called on the class, the list will contain two million Integer objects. These objects will never be eligible for garbage collection until the SomeClass class is unloaded. This will usually only happen when the application ends.

Let’s take out the two static keywords. The code now becomes:

import java.util.*;

public class SomeClass {
    private List<Integer> list = new ArrayList<>();

    public void fillList(int number) {
        Random r = new Random();
        for (int i=0; i<number; ++i) {
            list.add(r.nextInt());
        }
    }
} // end of class

We’ve now declared the list field without the static keyword. This means there is a list field for each SomeClass object. The fillList() method is now called on an object as follows:

SomeClass object = new SomeClass();
object.fillList(2_000_000);

When the SomeClass object is not longer in use and is dereferenced, the list field will be eligible for garbage collection. The two million Integer objects can then be removed from the heap.

Heavy use of static variables can potentially cause memory leaks. Every object that is reachable from a static variable can also potentially live forever.

As a side note, whether we declared the fillList() method as static or not is immaterial. The method operates the same way regardless. The way we invoke it will be different. The memory leak doesn’t happen in the method code, but with the list variable.

Leak or No Leak?

We only consider something to be a memory leak if we wanted the memory to be freed and it wasn’t. Let’s assume that we only want our static variable to reference an object for a part of the application runtime. If we forget to set it to null when we’re done with that object, we will likely end up with a memory leak.

But if we intend the static variable to reference an object for as long as the program is running, then it is most definitely not a leak. It is more likely to be a “permanent” singleton. If the object was reclaimed while we needed it to be available, that would have been even worse than a memory leak.

Insertion into Collections without Deletion

We need to be alert when we use collections in general. It’s very easy to unintentionally hold on to references for longer than we need to.

As mentioned earlier, adding objects to collections without deleting them is a common source of memory leaks. Often we forget to remove objects from a collection when we don’t need them any more. These unneeded objects are still being referenced, so the garbage collector can’t remove them.

Instead of directly referencing objects, we can use special reference objects that allow easy garbage collection. These reference objects are the SoftReference and WeakReference classes in the java.lang.ref package. These will be the topic of a future post.

Anonymous Inner Classes and Listeners

We often write GUI event listeners as anonymous inner classes. These are non-static inner classes. By default, a non-static inner class holds an implicit reference to its containing class. If we use this inner class object in our application, then even after our containing class object goes out of scope, it won’t be garbage collected.

As long as the listener is reachable it will continue to refer to the whole class. If we register an object as a listener and forget to deregister it, it can end up holding a reference to the outer class object. This object cannot be garbage collected, and a memory leak occurs.

If the inner class doesn’t need access to the containing class members, we should change it into a static class.

This is usually not a problem when we add listeners to components in a frame (AWT’s Frame or Swing’s JFrame). When that frame is disposed and is no longer referenced, all its components become unreachable. Thereafter everything will be garbage collected.

Conclusion

We’ll look at some more of the causes of memory leaks in the next post. These will include incorrect or missing equals() and hashCode() methods, and interning Strings.

Until then, stay healthy and keep learning!

Leave a Comment

Your email address will not be published. Required fields are marked *

Code like a Java Guru!

Thank You

We're Excited!

Thank you for completing the form. We're excited that you have chosen to contact us about training. We will process the information as soon as we can, and we will do our best to contact you within 1 working day. (Please note that our offices are closed over weekends and public holidays.)

Don't Worry

Our privacy policy ensures your data is safe: Incus Data does not sell or otherwise distribute email addresses. We will not divulge your personal information to anyone unless specifically authorised by you.

If you need any further information, please contact us on tel: (27) 12-666-2020 or email info@incusdata.com

How can we help you?

Let us contact you about your training requirements. Just fill in a few details, and we’ll get right back to you.

Your Java tip is on its way!

Check that incusdata.com is an approved sender, so that your Java tips don’t land up in the spam folder.

Our privacy policy means your data is safe. You can unsubscribe from these tips at any time.