a. Always try to
avoid generating garbage, because this will reduce the need to run a garbage
collection. Just think about three things:
1.Reuse
existing objects
2.Avoid
creating unnecessary objects
3.Create
object pools (Use Object Pool only when object creation is very expensive like
threads or data connection
b. Avoid using
finalizers when possible:
Whenever an object finalizer needs to be
executed, it gets placed into a finalization queue. On most systems, this queue
will be run only when a first level garbage collection fails. First level
garbage collection only fails when room for a new object cannot be found in the
heap or the overall free heap space drops below 25%. This behaviour causes all the
resources associated with that object to be retained until that time, which can
be many garbage collection cycles later. This time delay between queuing a
finalizer and actually running the finalizer can be considerable, causing
potential shortages of system resources. Not using a finalizer on the object
will avoid this delay.
c. Recycle and/ or
cache objects
The cost of object creation and garbage
collection is quite high. If existing objects can be reused, then savings in
memory and runtime performance can be realized. Look for opportunities in
writing code where existing objects can be reused and recycled instead of
creating new ones. Cache objects in frequently used methods so that they will
persist between calls, but make sure that the cached object’s state is set
properly before subsequent use. For example, if you cached a date object, then
prior to using it again, ensure that it is set to the proper date. Some objects
are not as straightforward as a date object, so use care.
d. Variables
In contrast to a compiled language such as C++,
application performance in Java is noticeably affected by what types of
variables are accessed and how they are accessed. For example, while stack
variables are directly addressable (and may even be placed in registers),
instance variables typically require an extra level of indirection to be
accessed.
This implies the potential value of data location
shifting, changing the storage location of data based on the access patterns.
For example, a data-intensive operation would benefit from first copying
instance variables into stack variables, operating on the stack variables, and,
finally, copying the stack variables back to the permanent instance variables.
This technique is particularly useful when a method
variable is accessed repeatedly within a loop. For example, the common loop
construct:
for (int i = 0; ++ i <= limit; )
can be improved by 25 percent (5 percent with a JIT
compiler) by rewriting it as:
for (int i = limit; — i >= 0; )
to reduce the number of accesses to the limit
variable.
e. Avoid synchronization
The JVM uses class locks while resolving class
references. Class references are resolved on an as used basis (versus resolving
at class load time) so it is not easy to predict when the JVM will want a class
lock. So, do not synchronize on a class.
An alternate approach would be to create a special
purpose lock object instead. For example:
private static final Object lock = new Object();
However, synchronization activity can consume
significant system resources and affect Java performance. This may be true even
if there is only one thread being executed, due to constant checking of lock
availability. As a result, synchronization should be reduced as much as
possible.
f. Use StringBuffer /StringBuilder
Since
strings are immutable, any change to a string will create at least one more
string object. This degrades performance and unnecessarily creates objects that
will eventually need to be garbage collected. StringBuffers, however, are
modifiable and can be used to avoid creating temporary String objects.