Programming errors can prevent garbage collection of objects that are no longer relevant to program operation. The garbage collector collects only unreachable objects; consequently, the presence of reachable objects that remain unused indicates memory mismanagement. Consumption of all available heap space can cause an OutOfMemoryError
, which usually results in program termination.
Excessive memory leaks can lead to memory exhaustion and denial of service (DoS) and must be avoided (see MSC05-J. Do not exhaust heap space for more information).
Noncompliant Code Example (Off-by-One Programming Error)
The vector
object in the noncompliant code example leaks memory. The condition for removing the vector
element is mistakenly written as n > 0
instead of n >= 0
. Consequently, the method fails to remove one element per invocation and quickly exhausts the available heap space.
Code Block | ||
---|---|---|
| ||
Wiki Markup | ||
Even though Java supports memory management through garbage collection, there are innumerable possibilities of introducing memory leaks as a result of 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 indicates memory mismanagement. Depending on program scale and available memory, an {{OutOfMemoryError}} may result when the heap space runs out. This usually results in program failure. h2. Noncompliant Code Example (Off-By-One Programming Error) This noncompliant code example shows a leaking {{Vector}} object. The memory leak quickly exhausts the heap space as the condition for removing the {{vector}} element is mistakenly written as {{n > 0}} instead of {{n >= 0}}. As a result, in every iteration, the method leaks one {{vector}} element. {code:bgColor=#FFCCCC} public class Leak { static Vector vector = new Vector(); public void useVector(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.useVector(1); i++; } } } {code} h2. Compliant Solution (1) This compliant solution corrects the mistake by changing the loop condition to {{n >= 0}}. {code:bgColor=#ccccff} |
Compliant Solution (>=
)
This compliant solution corrects the mistake by changing the loop condition to n >= 0
. It also wraps the cleanup code in a finally
block so that it still executes even if the interim code throws an exception.
Code Block | ||
---|---|---|
| ||
public void useVector(int count) { for (int n = 0; try { for (; n < count; n++) { vector.add(Integer.toString(n)); } // ... } finally { for (int n = countn - 1; n >= 0; n--) { vector.removeElementAt(n); } } {code} h2. |
Compliant
...
Solution
...
(clear(
...
)
)
...
Prefer
...
the
...
use
...
of
...
standard
...
language
...
semantics
...
where possible. This compliant solution uses the vector.clear()
method, which removes all elements:
Code Block | ||
---|---|---|
| ||
possible, as shown in this compliant solution. {code:bgColor=#ccccff} public void useVector(int count) { try { for (int n = 0; n < count; n++) { vector.add(Integer.toString(n)); } // ... while (!vector.isEmpty())} finally { vector.removeElementAt(vector.size() - 1); clear(); // Clear the vector } } {code} h2. Compliant Solution (3) An alternative way of clearing the {{vector}} is to use the {{vector.clear()}} method. {code:bgColor=#ccccff} public void useVector(int count) { |
Noncompliant Code Example (Nonlocal Instance Field)
This noncompliant code example declares and allocates a HashMap
instance field that is used only in the doSomething()
method:
Code Block | ||
---|---|---|
| ||
public class Storer { private HashMap<Integer,String> hm = new HashMap<Integer, String>(); private void doSomething() { // hm is used only here and never referenced again hm.put(1, "java"); for (int n = 0; n < count; n++) { vector.add(Integer.toString(n)); } // ... vector.clear(); // Clear the vector } {code} Likewise, if a range of elements have to be removed from the {{vector}}, {{vector.subList(fromIndex, toIndex).clear()}} can be used. In the context of this compliant solution, the {{fromIndex}} and the {{toIndex}} can both be {{0}} as the {{count}} variable is {{1}} on each iteration \[[API 2006|AA. Bibliography#API 06]\]. h2. Noncompliant Code Example (Non-Local Instance Field) This noncompliant code example creates a {{HashMap}} instance field within the class body but uses it only in the {{doSomething()}} method. {code:bgColor=#FFCCCC} public class Storer { private HashMap<Integer,String> hm = new HashMap<Integer, String>(); private void doSomething() { hm.put(1, "java"); // hm is used only here and never referenced again // ... } } {code} It is not obvious that it will continue to persist as long as the class {{Storer}}'s instance is alive. h2. Compliant Solution (Reduce Scope of Instance Field) This compliant solution declares the {{HashMap}} within the {{doSomething()}} method. {code:bgColor=#ccccff} public class Storer { private void doSomething() { HashMap<Integer,String> hm = new HashMap<Integer,String>(); hm.put(1,"java"); // ... } } {code} 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. h2. Noncompliant Code Example (Lapsed Listener) This noncompliant code example demonstrates 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. {code:bgColor=#FFCCCC} 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 } } {code} As a result, the garbage collector does not collect the {{reader}} object. A similar problem occurs with inner classes as they hold an implicit reference to the outer class. h2. Noncompliant Code Example (Exception Before Remove) {mc} Bloch 08 says: The best way to ensure that callbacks are garbage collected promptly is to store only weak references to them, for instance, by storing them only as keys in a WeakHashMap. {mc} To resolve this problem, a matching pair of the {{removeActionListener()}} should be used, as shown below. {code:bgColor=#FFCCCC} 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) { // Forward to handler } {code} 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. h2. Compliant Solution ({{finally}} Block) This compliant solution uses the {{finally}} block to ensure that the {{reader}} object's reference is unregistered. {code:bgColor=#ccccff} Reader reader = new Reader(); button.addActionListener(reader); try { reader.readSomething(); } catch (IOException e) { // Handle exception } finally { button.removeActionListener(reader); // Always executed } {code} h2. Noncompliant Code Example (Member Object Leaks) This noncompliant code example implements a {{stack}} data structure \[[Bloch 2008|AA. Bibliography#Bloch 08]\]. The main issue is that it does not allow the garbage collector to deallocate memory after the {{pop}} operation. {code:bgColor=#FFCCCC} 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); } } } {code} 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 because none of the objects referenced by the offending object get garbage collected. h2. Compliant Solution (Assign {{null}} to Elements of Data Structures) This compliant solution assigns {{null}} values to all obsolete references. {code:bgColor=#ccccff} public Object pop() { if (size==0) throw new EmptyStackException(); // Ensures object consistency Object result = elements[--size]; elements[size] = null; // Eliminate obsolete reference return result; } {code} The garbage collector can include individual element-objects in its list of objects to free. A {{NullPointerException}} results on subsequent attempts to access the particular object. While these examples may not model production scenarios, it is not uncommon to uncover _obsolete references_ when dealing with data structures such as hash tables containing 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 has no specific advantages. The garbage collector is sufficiently equipped to handle these cases \[[Commes 2007|AA. Bibliography#Commes 07]\]. h2. Noncompliant Code Example (Strong References) A common variant of the aforementioned noncompliant code example is the unintentional retention of objects when using a {{Map}} or a similar {{Collections}} object. In this noncompliant code example, a server maintains temporary metadata about all secure connections it commits to. {code:bgColor=#FFCCCC} class HashMetaData { private Map<SSLSocket, InetAddress> m = Collections.synchronizedMap(new HashMap<SSLSocket, InetAddress>()); public void storeTempConnection(SSLSocket sock, InetAddress ip) { m.put(sock, ip); } public void removeTempConnection(SSLSocket sock) { m.remove(sock); } } {code} Although the metadata is designed to be {{transient}} by nature, it persists even after the particular socket is closed, until {{removeTempConnection()}} is invoked. Unless some notification logic is installed, it is impossible to determine the best time to eliminate the object reference by calling {{removeTempConnection()}}. Moreover, nulling out original objects or referents (Socket connections) proves to be unwieldy. h2. Compliant Solution (Weak References) This compliant solution uses _weak references_ to mitigate the issue. {code:bgColor=#ccccff} // ... private Map<SSLSocket, InetAddress> m = Collections.synchronizedMap(new WeakHashMap<SSLSocket, InetAddress>()); {code} Strong references do not allow the garbage collector to reclaim the objects that are stored compositely, such as in a {{Map}}. According to the Java API \[[API 2006|AA. Bibliography#API 06]\], weak reference objects: "... do not prevent their referents from being made finalizable, finalized, and then reclaimed." A referent is the object that is being referred to. As soon as any strong references to the object are found to have phased out, the garbage collector reclaims the referent. With {{WeakHashMap}}, the map's _key_ is weakly referred to and as a result determines whether the corresponding referents are ready to be collected. An object becomes eligible for garbage collection when only weak references to the referent exist. A weak reference allows the code to refer to the referent without holding its garbage collection. This approach is only suitable when the lifetime of the object is required to be the same as the lifetime of the key. It is not enough to facilitate the garbage collection of unneeded objects with weak references. It is critical to prune the data structure so that more entries can be accommodated in the newly created space. This can be achieved by calling the {{get()}} method of {{WeakHashMap}} and removing the entry that corresponds to the {{null}} return value (polling). A more efficient method is to use a reference queue \[[Goetz 2005b|AA. Bibliography#Goetz 05b]\]. h2. Compliant Solution (Reference Queue) Reference queues provide a way to receive notifications when a referent is garbage collected. If the referent is assigned the value {{null}}, it is eventually garbage collected. However, the {{HashMap}} continues to strongly reference the {{WeakReference}} object and the corresponding value (for each entry in the {{HashMap}}). As soon as the GC clears the reference (which referred to the referent), it adds the corresponding {{WeakReference}} object to the reference queue. It remains there unless some operation is performed on the queue (such as a {{put()}} or {{remove()}}). After such an operation, the {{WeakReference}} object in the hashmap is also collected. Alternatively, this two-step procedure can be carried out manually by using the following code: {code:bgColor=#ccccff} class HashMetaData { private Map<WeakReference<SSLSocket>, InetAddress> m = Collections.synchronizedMap(new HashMap<WeakReference<SSLSocket>, InetAddress>()); ReferenceQueue queue = new ReferenceQueue(); public void storeTempConnection(SSLSocket sock, InetAddress ip) { WeakReference<SSLSocket> wr = new WeakReference<SSLSocket>(sock, queue); while ((wr = (WeakReference) queue.poll()) != null) { // poll for dead entries before adding more m.remove(wr); // Removes the WeakReference object and the value (not the referent) } m.put(wr, ip); } public void removeTempConnection(SSLSocket sock) { m.remove(sock); } } {code} Note that the two-argument constructor of {{WeakReference}} takes a {{Queue}} argument and must be used to perform direct queue processing. Dead entries may be pruned prior to insertion. h2. Compliant Solution (Soft References) It is also permissible to use soft references because they guarantee that the referent will be reclaimed before an {{OutOfMemoryError}} results but no sooner than the time when memory begins to run out. {code:bgColor=#ccccff} class HashMetaData { private Map<SoftReference<SSLSocket>, InetAddress> m = Collections.synchronizedMap(new HashMap<SoftReference<SSLSocket>, InetAddress>()); ReferenceQueue queue = new ReferenceQueue(); public void storeTempConnection(SSLSocket sock, InetAddress ip) { SoftReference<SSLSocket> sr = new SoftReference<SSLSocket>(sock, queue); while ((sr = (SoftReference) queue.poll()) != null) { m.remove(sr); // Removes the WeakReference object and the value (not the referent) } m.put(sr, ip); } public void removeTempConnection(SSLSocket sock) { m.remove(sock); } } {code} Soft references are preferred over weak references for applications such as caching because weak references are garbage collected more aggressively, to the point that they become unsuitable. h2. Risk Assessment Memory leaks in Java applications may be exploited to cause denial of service. || Guideline || Severity || Likelihood || Remediation Cost || Priority || Level || | MSC06-J | low | unlikely | high | {color:green}{*}P1{*}{color} | {color:green}{*}L3{*}{color} | h3. Automated Detection TODO h3. Related Vulnerabilities Search for vulnerabilities resulting from the violation of this guideline on the [CERT website|https://www.kb.cert.org/vulnotes/bymetric?searchview&query=FIELD+KEYWORDS+contains+MSC01-J]. h2. Bibliography \[[API 2006|AA. Bibliography#API 06]\] Class Vector, Class WeakReference \[[Gupta 2005|AA. Bibliography#Gupts 05]\] \[[Bloch 2008|AA. Bibliography#Bloch 08]\] Item 6: Eliminate obsolete object references \[[Commes 2007|AA. Bibliography#Commes 07]\] Memory Leak Avoidance \[[Goetz 2005|AA. Bibliography#Goetz 05]\] Lapsed listeners \[[Goetz 2005b|AA. Bibliography#Goetz 05b]\] "Memory leaks with global Maps" and "Reference queues" \[[MITRE 2009|AA. Bibliography#MITRE 09]\] [CWE ID 401|http://cwe.mitre.org/data/definitions/401.html] "Failure to Release Memory Before Removing Last Reference (aka 'Memory Leak')" ---- [!The CERT Oracle Secure Coding Standard for Java^button_arrow_left.png!|OBJ02-J. Make sensitive classes noncloneable] [!The CERT Oracle Secure Coding Standard for Java^button_arrow_up.png!|49. Miscellaneous (MSC)] [!The CERT Oracle Secure Coding Standard for Java^button_arrow_right.png!|DCL12-J. Prevent class initialization cycles] } |
Programmers may be surprised that the HashMap
persists for the entire lifetime of the Storer
instance.
Compliant Solution (Reduce Scope of Instance Field)
This compliant solution declares the HashMap
as a local variable within the doSomething()
method. The hm
local variable is eliminated after the method returns. When the local variable holds the only reference to the HashMap
, the garbage collector can reclaim its associated storage.
Code Block | ||
---|---|---|
| ||
public class Storer {
private void doSomething() {
HashMap<Integer,String> hm = new HashMap<Integer,String>();
hm.put(1,"java");
// ...
}
}
|
Localizing or confining the instance field to a narrower scope simplifies garbage collection; today's generational garbage collectors perform well with short-lived objects.
Noncompliant Code Example (Lapsed Listener)
This noncompliant code example, known as the Lapsed Listener [Goetz 2005a], demonstrates unintentional object retention. The button
continues to hold a reference of the reader
object after completion of the readSomething()
method, even though the reader
object is never used again. Consequently, the garbage collector cannot collect the reader
object. A similar problem occurs with inner classes because they hold an implicit reference to the enclosing 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
}
}
|
Noncompliant Code Example (Exception before Remove)
This noncompliant code example attempts to remove the reader
through use of the removeActionListener()
method:
Code Block | ||
---|---|---|
| ||
Reader reader = new Reader();
button.addActionListener(reader);
try {
reader.readSomething(); // Can skip next line of code
// Dereferenced, but control flow can change
button.removeActionListener(reader);
} catch (IOException e) {
// Forward to handler
}
|
If an exception is thrown by the readSomething()
method, the removeActionListener()
statement is never executed.
Compliant Solution (finally
Block)
This compliant solution uses a finally
block to ensure that the reader
object's reference is removed:
Code Block | ||
---|---|---|
| ||
Reader reader = new Reader();
button.addActionListener(reader);
try {
reader.readSomething();
} catch (IOException e) {
// Handle exception
} finally {
button.removeActionListener(reader); // Always executed
}
|
Noncompliant Code Example (Member Object Leaks)
This noncompliant code example implements a stack data structure [Bloch 2008] that continues to hold references to elements after they have been popped off the stack:
Code Block | ||
---|---|---|
| ||
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);
}
}
}
|
The object references are retained on the stack even after the element is popped. Such obsolete references cause objects to remain live; consequently, the objects cannot be garbage-collected.
Compliant Solution (null
)
This compliant solution assigns null
to all obsolete references:
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;
}
|
The garbage collector can then include individual objects formerly referenced from the stack in its list of objects to free.
Although these examples appear trivial and do not represent significant problems in production code, obsolete references remain a concern when dealing with data structures such as hash tables containing many large records. It is prudent to assign null
to array-like custom data structures; doing so with individual objects references or local variables is unnecessary because the garbage collector handles these cases automatically [Commes 2007].
Noncompliant Code Example (Strong References)
A common variation of the obsolete object fallacy is the unintentional retention of objects in collections such as maps. In this noncompliant code example, a server maintains temporary metadata about all committed secure connections:
Code Block | ||
---|---|---|
| ||
class HashMetaData {
private Map<SSLSocket, InetAddress> m = Collections.synchronizedMap(
new HashMap<SSLSocket, InetAddress>());
public void storeTempConnection(SSLSocket sock, InetAddress ip) {
m.put(sock, ip);
}
public void removeTempConnection(SSLSocket sock) {
m.remove(sock);
}
}
|
It is possible to close a socket without removing it from this map. Consequently, this map may contain dead sockets until removeTempConnection()
is invoked on them. In the absence of notification logic, it is impossible to determine when to call removeTempConnection()
. Moreover, nullifying original objects or referents (Socket
connections) is unwieldy.
Compliant Solution (Weak References)
This compliant solution uses weak references to allow timely garbage collection:
Code Block | ||
---|---|---|
| ||
// ...
private Map<SSLSocket, InetAddress> m = Collections.synchronizedMap(
new WeakHashMap<SSLSocket, InetAddress>());
// ... |
Strong references prevent the garbage collector from reclaiming objects that are stored inside container objects, such as in a Map
. According to the Java API [API 2014], weak reference objects "do not prevent their referents from being made finalizable, finalized, and then reclaimed."
Keys held in WeakHashMap
objects are referenced through weak references. Objects become eligible for garbage collection when they lack strong references. Consequently, use of weak references allows the code to refer to the referent without delaying garbage collection of the referent. This approach is suitable only when the lifetime of the object is required to be the same as the lifetime of the key.
Simply facilitating garbage collection of unneeded objects through use of weak references is insufficient. Programs must also prune the recording data structure so that additional live entries can be accommodated. The implementation of WeakHashMap
in Java 7 includes a reference queue to efficiently remove entries that correspond to a null pointer value [https://github.com/openjdk-mirror/jdk7u-jdk/blob/master/src/share/classes/java/util/WeakHashMap.java].
Compliant Solution (Soft References)
Use of soft references is also permitted. Soft references guarantee that the referent will be reclaimed before an OutOfMemoryError
occurs and also that the referent will remain live until memory begins to run out.
Reference queues provide notifications when a referent is garbage-collected. When the referent is garbage-collected, the HashMap
continues to strongly reference both the SoftReference
object and the corresponding map value (for each entry in the HashMap
).
When the garbage collector clears the reference to an object, it adds the corresponding SoftReference
object to the reference queue. The SoftReference
object remains in the reference queue until some operation is performed on the queue (such as a poll()
or remove()
). After such an operation, the SoftReference
object in the hash map is also garbage-collected:
Code Block | ||
---|---|---|
| ||
class HashMetaData {
private Map<SoftReference<SSLSocket>, InetAddress> m =
Collections.synchronizedMap(
new HashMap<SoftReference<SSLSocket>, InetAddress>());
ReferenceQueue queue = new ReferenceQueue();
public void storeTempConnection(SSLSocket sock, InetAddress ip) {
SoftReference<SSLSocket> sr;
while ((sr = (SoftReference) queue.poll()) != null) {
// Removes the WeakReference object and the value (not the referent)
m.remove(sr);
}
sr = new SoftReference<SSLSocket>(sock, queue);
m.put(sr, ip);
}
// removeTempConnection() deleted, no longer necessary
}
|
Note that the two-argument constructor of SoftReference
takes a Queue
argument and must be used to perform direct queue processing. Dead entries should be pruned prior to insertion.
Weak references are garbage-collected more aggressively than soft references. Consequently, weak references should be preferred in applications in which efficient memory usage is critical, and soft references should be preferred in applications that rely heavily on caching.
Risk Assessment
Memory leaks in Java applications may be exploited in a DoS attack.
Rule | Severity | Likelihood | Remediation Cost | Priority | Level |
---|---|---|---|---|---|
MSC04-J | Low | Unlikely | High | P1 | L3 |
Automated Detection
Tool | Version | Checker | Description | ||||||
---|---|---|---|---|---|---|---|---|---|
Parasoft Jtest |
| CERT.MSC04.LEAKS | Ensure resources are deallocated |
Related Guidelines
Memory Leak [XYL] | |
CWE-401, Improper Release of Memory before Removing Last Reference ("Memory Leak") |
Bibliography
[API 2014] | |
Item 6, "Eliminate Obsolete Object References" | |
"Memory Leak Avoidance" | |
"Lapsed Listeners" | |
...