Even though Java supports memory management through garbage collection, there are innumerable possibilities of introducing memory leaks due to programming errors. Furthermore, the garbage collector collects only the unreachable objects and not those that are still reachable. The presence of reachable objects that remain unused clearly call for memory mismanagement.
programmer committed mistakes. Depending on program scale and available memory, one of the most undesired errors, the OutOfMemoryError
may manifest itself wherein the heap space runs out causing program failure.
...
This noncompliant example shows a leaking vector
object. This quickly exhausts the heap space as the programmer has mistakenly written the condition for removing the vector element as n>0
instead of n>=0
. As a result, in every iteration the method leaks one vector element.
Code Block |
---|
|
import java.util.Vector;
import java.io.IOException;
public class Leak {
static Vector vector = new Vector();
public void leakingVector(int count) {
for (int n=0; n<count; n++) {
vector.add(Integer.toString(n));
}
for (int n=count-1; n>0; n--) { //free the memory
vector.removeElementAt(n);
}
}
public static void main(String[] args) throws IOException {
Leak le = new Leak();
int i = 1;
while(true) {
System.out.println("Iteration: " + i);
le.leakingVector(1);
i++;
}
}
}
|
...
Code Block |
---|
|
vector.clear();
|
Noncompliant Code Example
This noncompliant example creates a HashMap
instance field within the class body but uses it only in the doSomething
method. Sometimes, it is not obvious that it will continue to persist as long as class BadScope
's instance is alive.
Code Block |
---|
|
public class BadScope {
private HashMap<Integer,String> hm = new HashMap<Integer,String>();
private void doSomething() {
hm.put(1,"java"); // hm is used only here
}
}
|
Compliant Solution
Localizing or confining the instance field to a narrower scope gives the garbage collector a better chance of succeeding at collecting the object in a timely manner. Short-lived objects are always collected quickly by generational garbage collectors.
Code Block |
---|
|
public class GoodScope {
private void doSomething() {
HashMap<Integer,String> hm = new HashMap<Integer,String>();
hm.put(1,"java");
}
}
|
Noncompliant Code Example
This noncompliant code snippet is an example of unintentional object retention and is commonly called the Lapsed Listener. The button
continues to hold a reference of the reader
object even after completion of the readSomething
method. Thus, the garbage collector will not collect the reader
object. A similar problem occurs with inner classes as they hold an implicit reference to the outer class.
Code Block |
---|
|
public class LapseEvent extends JApplet {
JButton button;
public void init() {
button = new JButton("Click Me");
getContentPane().add(button, BorderLayout.CENTER);
Reader reader = new Reader();
button.addActionListener(reader);
try {
reader.readSomething();
} catch (IOException e) { /* handle exception */ }
}
}
class Reader implements ActionListener{
public void actionPerformed(ActionEvent e) {
Toolkit.getDefaultToolkit().beep();
}
public void readSomething() throws IOException {
// read from file
}
}
|
To solve this problem, a matching pair of the removeActionListener
should be used, as shown below. Unfortunately, this is not the panacea because an exception in the reader.readSomething
method can change the control flow in such a way that the removeActionListener
statement is never executed.
Code Block |
---|
|
Reader reader = new Reader();
button.addActionListener(reader);
try {
reader.readSomething(); // can skip next line
button.removeActionListener(reader); // dereferenced, but control flow can change
} catch (IOException e) { /* handle exception */ }
|
Compliant Solution
The solution is to use the finally
block in order to ensure that the reader
object's reference is unregistered.
Code Block |
---|
|
Reader reader = new Reader();
button.addActionListener(reader);
try {
reader.readSomething();
} catch (IOException e) { /* handle exception */ }
finally {
button.removeActionListener(reader); // always gets executed
}
|
Noncompliant Code Example
Wiki Markup |
---|
This example implements a {{stack}} data structure \[[Bloch 08|AA. Java References#Bloch 08]\] Item 6: Eliminate obsolete object references. The main issue is that it does not allow the garbage collector to de-allocate memory after the {{pop}} operation. The object references are retained even after the element is pop'ed. Such _obsolete references_ are not garbage collected automatically. This can get even more deceitful since none of the objects referenced by the offending object get garbage collected either. |
Code Block |
---|
|
import java.util.EmptyStackException;
public class Stack {
private Object[] elements;
private int size = 0;
public Stack(int initialCapacity) {
this.elements = new Object[initialCapacity];
}
public void push(Object e) {
ensureCapacity();
elements[size++] = e;
}
public Object pop() { //this method causes memory leaks
if (size == 0)
throw new EmptyStackException();
return elements[--size];
}
/**
* Ensure space for at least one more element, roughly
* doubling the capacity each time the array needs to grow.
*/
private void ensureCapacity() {
if (elements.length == size) {
Object[] oldElements = elements;
elements = new Object[2 * elements.length + 1];
System.arraycopy(oldElements, 0, elements, 0, size);
}
}
}
|
Compliant Solution
This compliant solution assigns null
values to all obsolete references. The garbage collector can now include this object in its list of objects to free. A NullPointerException
exception results on subsequent attempts to access the particular object.
Code Block |
---|
|
public Object pop() {
if (size==0)
throw new EmptyStackException(); // Ensures object consistency
Object result = elements[--size];
elements[size] = null; // Eliminate obsolete reference
return result;
}
|
Wiki Markup |
---|
While these examples may not model production scenarios, it is not uncommon to have _obsolete references_ when dealing with data structures such as hash tables that contain many large-sized records. It is prudent to assign {{null}} to array-like custom data structures, however, doing so with individual objects or local variables gives no specific advantage. The garbage collector is sufficiently equipped to handle these cases. \[[Commes 07|AA. Java References#Commes 07]\] |
Risk Assessment
Memory leaks in Java applications may be exploited, resulting in denial-of-service attacks.
...
Wiki Markup |
---|
\[[API 06|AA. Java References#API 06]\] Class Vector
\[[Gupta 05|AA. Java References#Gupts 05]\]
\[[Bloch 08|AA. Java References#Bloch 08]\] Item 6: Eliminate obsolete object references
\[[Commes 07|AA. Java References#Commes 07]\] Memory Leak Avoidance
\[[Goetz 05|AA. Java References#Goetz 05]\] Lapsed listeners |
...
MSC00-J. Eliminate class initialization cycles 11. Miscellaneous (MSC) MSC02-J. Be aware of the JVM Tool Interface