...
Use Short-lived Immutable Objects
Wiki Markup |
---|
Since JDK 1.2, the new generational garbage collector has reduced memory allocation related costs to minimal levels, even lesser than C/C++. Deallocation has also become cheaper |
such that the cost of garbage collection is commensurate with the number of _live_ objects in the _younger generation_ and not the _total_ number of objects allocated since the last run. Memory is managed in generations to optimize the collection. The younger generation consists of short-lived objects. A minor collection on the younger generation is performed when it fills up with dead objects. \[[Oracle 10a|AA. Java References#Oracle 10a]\] |
Wiki Markup |
---|
Note that objects in the _younger generation_ that persist for longer durations are _tenured_ and are moved to the _tenured generation_. Very few _younger generation_ objects continue to live through to the next garbage collection cycle; the rest become ready to be collected in the impending collection cycle. \[[Oracle 10a|AA. Java References#Oracle 10a]\] |
With generational GCs it is advantageous to use short-lived immutable objects instead of long-lived mutable objects. Object pools are examples of the latter and should as a result be avoided to increase the garbage collector's efficiency. Moreover, object pools can create synchronization problems, deallocations have to be managed explicitly leading to dangers of dangling pointers, and the size of the pool also plays a dominant role in mission critical code. Exceptions to this recommendation can be made when the allocation takes longer in comparison, such as while when performing multiple joins across databases or while when using objects that represent scarce resources such as thread pools and database connections.
...
Code Block | ||
---|---|---|
| ||
public class MutableHolder { private Hashtable<Integer, String> value; // not final public Object getValue() { return value; } public void setValue(Hashtable<Integer, String> ht) { value = (Hashtable<Integer, String>)ht; } } |
This example also violates OBJ11FIO00-J. Defensively copy private mutable class members before returning their referencesmutable inputs and mutable internal components.
Compliant Solution
This compliant solution highlights a custom container called ImmutableHolder
. To aid garbage collection, it is recommended that short-lived ImmutableHolder
objects be created by passing Hashtable
instances to the constructor. When value
is assigned in ImmutableHolder
's constructor during object creation, it is a younger member field (of type ImmutableHolder.Hashtable<Integer, String>
) that is referencing an older object (of type Hashtable<Integer, String>). This is a much better position to be in as far as the garbage collector is concerned. Note that a shallow copy is used in this case to preserve references to the older value.
Code Block | ||
---|---|---|
| ||
public class ImmutableHolder { private final Hashtable<Integer, String> value; // create defensive copy of inputs public ImmutableHolder(Hashtable<Integer, String> ht) { value = (Hashtable<Integer, String>)ht.clone(); } // create defensive copy while returning public Object getValue() { return value.clone(); } } |
When value
is assigned in ImmutableHolder
's constructor during object creation, it is a younger member field (of type ImmutableHolder.Hashtable<Integer, String>
) that is referencing an older object in the client code (of the same type Hashtable<Integer, String>
). This is a much better position to be in as far as the garbage collector is concerned. Note that a shallow copy is used in this case to preserve references to the older value.
Avoid Large Objects
The allocation for large objects is expensive and initializing (zeroing) also takes timethe initialization cost is proportional to their size. Sometimes large objects of different sizes can cause fragmentation issues or non compacting collect.
...
Code Block | ||
---|---|---|
| ||
ByteBuffer buffer = ByteBuffer.allocate(8192); // use buffer once |
Nulling References
...
Reference nulling to "help the garbage collector" is unnecessary. In fact, it just adds clutter to the code and sometimes introduces subtle bugs. Assigning null
to local variables is also not very useful as the Java Just-In-Time compiler (JIT) can equivalently do a liveness analysis. A related bad practice is to use a finalizer to null
out references. This practice can cause a huge performance hit.
Noncompliant Code Example
This noncompliant code example assigns null
to the buffer
array.
Code Block | ||
---|---|---|
| ||
int[] buffer = new int[100]; doSomething(buffer); buffer = null // No need to explicitly assign null |
...