loading...

Effective Java Tuesday! Don't Leak Object References!

kylec32 profile image Kyle Carter ・3 min read

Effective Java Review (36 Part Series)

1) Effective Java Tuesday! Let's Consider Static Factory Methods 2) Effective Java Tuesday! The Builder Pattern! 3 ... 34 3) Effective Java Tuesday! Singletons! 4) Effective Java Tuesday! Utility Classes! 5) Effective Java Tuesday! Prefer Dependency Injection! 6) Effective Java Tuesday! Avoid Creating Unnecessary Objects! 7) Effective Java Tuesday! Don't Leak Object References! 8) Effective Java Tuesday! Avoid Finalizers and Cleaners! 9) Effective Java Tuesday! Prefer try-with-resources 10) Effective Java Tuesday! Obey the `equals` contract 11) Effective Java Tuesday! Obey the `hashCode` contract 12) Effective Java Tuesday! Override `toString` 13) Effective Java Tuesday! Override `clone` judiciously 14) Effective Java Tuesday! Consider Implementing `Comparable` 15) Effective Java Tuesday! Minimize the Accessibility of Classes and Member 16) Effective Java Tuesday! In Public Classes, Use Accessors, Not Public Fields 17) Effective Java Tuesday! Minimize Mutability 18) Effective Java Tuesday! Favor Composition Over Inheritance 19) Effective Java Tuesday! Design and Document Classes for Inheritance or Else Prohibit It. 20) Effective Java Tuesday! Prefer Interfaces to Abstract Classes 21) Effective Java! Design Interfaces for Posterity 22) Effective Java! Use Interfaces Only to Define Types 23) Effective Java! Prefer Class Hierarchies to Tagged Classes 24) Effective Java! Favor Static Members Classes over Non-Static 25) Effective Java! Limit Source Files to a Single Top-Level Class 26) Effective Java! Don't Use Raw Types 27) Effective Java! Elminate Unchecked Warnings 28) Effective Java! Prefer Lists to Array 29) Effective Java! Favor Generic Types 30) Effective Java! Favor Generic Methods 31) Effective Java! Use Bounded Wildcards to Increase API Flexibility 32) Effective Java! Combine Generics and Varargs Judiciously 33) Effective Java! Consider Typesafe Hetergenous Containers 34) Effective Java! Use Enums Instead of int Constants 35) Effective Java! Use Instance Fields Instead of Ordinals 36) Effective Java! Use EnumSet Instead of Bit Fields

Today our topic is about memory leaks inside of Java. Wait!? Memory leaks in Java? That's not possible right? Isn't that one of the promises of Java, to have managed memory? When I started learning to code I started with C++ like man people. Let me tell you something, if you don't manage your memory well in C++ you will likely blow your foot off. But anyway we were talking about memory leaks in Java. I think the best way to show this is with an example and I think the example from the book is a great example of it:

public class Stack {
  private Object[] elements;
  private int size = 0;
  private static final int DEFAULT_INITIAL_SIZE = 16;

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

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

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

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

Looks like it would work right? I'll let you in on a secret. This code does actually work fine but it does have a "memory leak". Can you spot it? I for one definitely didn't get it right off the bat. The problem lies in this line here from our pop function.:

return elements[--size];

So what is wrong with this line? What the code is conceptually doing is returning an object and removing it from the stack. What is actually happening is that the elements array still has a reference to the object and won't be able to be garbage collected until that element is overwritten. So if, for example, a bunch of objects were added to the stack and then popped off we would expect the elements to be garbage collected but they will not.

But is this actually a memory leak? Not in the normal meaning of the word. More correctly they could be titled "obsolete object references." That being said the symptoms and problems it can cause are along the same lines. So how do we fix this issue. Well simply enough we null out the object before returning:

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

So am I saying that you should null out all objects after you are done with it? Please no. Having to null out objects is almost always the exception and not the rule. Java is indeed a language with managed memory and it will handle the clean up of our objects as they fall out of scope and lose all references to them. So in what cases do we need to account for the above issue and when do we not? It all comes down to if we are managing our own memory/objects or not. If you manage your memory you need to take it all the way and manage your memory, not just do it halfway. Above we are managing our memory ourselves as we have our array of elements that we are managing as storage.

What are some other places we see this. Caches is another example. We need to be aware of the lifecycle of our data in our caches when we use them as our cache can keep an object from being garbage collected even if nothing else in the system will ever request the object from the cache. One way to account for this is using Java's WeakReference class. We do need to be aware that when using this class it is simply a pointer to the object and is counting on an external system having a reference to the object to keep it from being garbage collected while it is still usable. Using WeakReferences and related classes are a more advanced topic and how to intelligently use it is beyond the scope of this blog article. A final location that developers can leave around obsolete objects is when using listeners and other callbacks. I remember this being of particular concern when working with Android in a previous life of mine. This can be accounted for in the same manner as above with the use of WeakReferences

The topic of discussion today is a little more niche without a doubt. You will likely not run into this everyday but this is one of the things to keep in mind as we develop our software. Memory leaks are extremely hard to debug and thus we should do all in our power to avoid them.

Effective Java Review (36 Part Series)

1) Effective Java Tuesday! Let's Consider Static Factory Methods 2) Effective Java Tuesday! The Builder Pattern! 3 ... 34 3) Effective Java Tuesday! Singletons! 4) Effective Java Tuesday! Utility Classes! 5) Effective Java Tuesday! Prefer Dependency Injection! 6) Effective Java Tuesday! Avoid Creating Unnecessary Objects! 7) Effective Java Tuesday! Don't Leak Object References! 8) Effective Java Tuesday! Avoid Finalizers and Cleaners! 9) Effective Java Tuesday! Prefer try-with-resources 10) Effective Java Tuesday! Obey the `equals` contract 11) Effective Java Tuesday! Obey the `hashCode` contract 12) Effective Java Tuesday! Override `toString` 13) Effective Java Tuesday! Override `clone` judiciously 14) Effective Java Tuesday! Consider Implementing `Comparable` 15) Effective Java Tuesday! Minimize the Accessibility of Classes and Member 16) Effective Java Tuesday! In Public Classes, Use Accessors, Not Public Fields 17) Effective Java Tuesday! Minimize Mutability 18) Effective Java Tuesday! Favor Composition Over Inheritance 19) Effective Java Tuesday! Design and Document Classes for Inheritance or Else Prohibit It. 20) Effective Java Tuesday! Prefer Interfaces to Abstract Classes 21) Effective Java! Design Interfaces for Posterity 22) Effective Java! Use Interfaces Only to Define Types 23) Effective Java! Prefer Class Hierarchies to Tagged Classes 24) Effective Java! Favor Static Members Classes over Non-Static 25) Effective Java! Limit Source Files to a Single Top-Level Class 26) Effective Java! Don't Use Raw Types 27) Effective Java! Elminate Unchecked Warnings 28) Effective Java! Prefer Lists to Array 29) Effective Java! Favor Generic Types 30) Effective Java! Favor Generic Methods 31) Effective Java! Use Bounded Wildcards to Increase API Flexibility 32) Effective Java! Combine Generics and Varargs Judiciously 33) Effective Java! Consider Typesafe Hetergenous Containers 34) Effective Java! Use Enums Instead of int Constants 35) Effective Java! Use Instance Fields Instead of Ordinals 36) Effective Java! Use EnumSet Instead of Bit Fields

Posted on by:

kylec32 profile

Kyle Carter

@kylec32

Backend Architect at MasterControl

Discussion

markdown guide