Hi all,
After a while without publish anything, due family growing and moving to other country once again, I'm back now publishing some tips for better code, maintainability and some that will help you keep your memory usage footprint low. Of course if the project has a not very optimal architecture will be hard just with code keep low memory footprint, though good code always helps.
This one is long, I have read articles from Oracle, across the web and the Java Performance Book (
Link for Amazon Book) that I recommend and it's a really good book for those that want to have better performance with Java.
In this post, nearly all tips and good practices are for code, I hope this helps those one looking for improve their code or trying to set a bar for the minimum acceptable code quality.
Things to avoid at all cost in code,
this is a basic thing and it’s an issue in most projects.
As the very first thing I would like to say to the
developer to do is don’t be lazy in create object, variables or any other reference
names, don’t make economy, use names that explain for any person what that
reference.variable name really means, don’t expect that others guess what you were thinking
earlier, not even make the mistake to think that you will remember exactly
what the short naming mean after a couple of months doing something else
in the project, be clear and verbose in explain a declaration.
E.g.:
// Don’t
public class BfProcCustAss {…
Object custAssActTdy = getCustomer();
// Do
public class BackfileProccessForCustmerAssociated {…
Object customerAssociatedActiveToday = getCustomer();
Avoid duplicated strings in the code as it is bad for code
maintenance and memory, producing fragmented memory.
// Don’t – here you
have two objects without any variable or instance variable association
If (customerType.equals(“Active”)) {
foo.callCustomer(“Active”);
….
}
// Do – here you have
one String object and two references to it
private static final String ACTIVE = “Active”;
// or
private final String ACTIVE = “Active”;
….
If (customerType.equals(ACTIVE)) {
foo.callCustomer(ACTIVE);
….
}
NO, magical numbers please!
Try avoid those numbers that make so much sense to you at
the code conception time though after a while not even you will be able to
understand what it means.
E.g.:
final List<Object> myDifferentObjectList = getHeterogeneousObjectList();
…
// Don’t
BeanOne beanOne = myDifferentObjectList.get(5);
// Do
// Declare it in your method as var or as instance variable
in your class
Private static final int BEAN_ONE = 5;
// Then
BeanOne beanOne = myDifferentObjectList.get(BEAN_ONE);
Or
// Don’t
cs.setString(2, feedToLookUp);
cs.setString(3, feedType);
cs.registerOutParameter(4, OracleTypes.CURSOR); // What are
you getting from here?
cs.registerOutParameter(5, OracleTypes.VARCHAR); // and
here?
// Do
final int PI_FEED_LOOK_UP = 2;
final int PI_FEED_TYPE = 3;
final int PO_RC_FEED_LIST = 4;
final int PO_MESSAGE_GROUP_ID = 5;
cs.setString(PI_FEED_LOOK_UP, feedToLookUp);
cs.setString(PI_FEED_TYPE, feedType);
cs.registerOutParameter(PO_RC_FEED_LIST,
OracleTypes.CURSOR); // Easier no?
cs.registerOutParameter(PO_MESSAGE_GROUP_ID,
OracleTypes.VARCHAR);
Those tips above
should be used with any language, not been restricted to Java, it’s a minimum
for provide some code insight to the next developer that will maintain the code
made by you.
The tips bellow are good for C++ and Java, however a few
will be Java only as C++ has a quite better set for work with Threads than Java
does, even today with the new Fork and Join released in JDK 7.
Avoid or Minimize Synchronization
Many performance studies have shown a high performance cost
in using synchronization in Java. Improper synchronization can also cause a
deadlock, which can result in complete loss of service because the system
usually has to be shut down and restarted. But performance overhead cost is not
a sufficient reason to avoid synchronization completely. Failing to make sure
your application is thread-safe in a multi-threaded environment can cause data
corruption, which can be much worse than losing performance. The following are
some practices that you can consider to minimize the overhead:
Synchronize Critical Sections Only
Do Not Use the Same Lock on Objects That Are Not Manipulated
Together
Use Private Fields
Use a Thread Safe Wrapper
Use Immutable Objects
Know Which Java Objects Already Have Synchronization
Built-in
Do Not Under-Synchronize
Synchronize Critical Sections Only
If only certain operations in the method must be
synchronized, use a synchronized block with a mutex instead of synchronizing
the entire method. For example:
private Object mutex = new Object();
...
private void
doSomething()
{
// perform tasks
that do not require synchronicity
...
synchronized
(mutex)
{
...
}
...
}
Do Not Use the Same Lock on Objects
That Are Not Manipulated Together
Every Java object has a single lock associated with it. If
unrelated operations within the class are forced to share the same lock, then
they have to wait for the lock and must be executed one at a time. In this
case, define a different mutex for each unrelated operation that requires
synchronization.
Also, do not use the same lock to restrict access to objects
that will never be shared by multiple threads. For example, using Hashtables to
store objects that will never be accessed concurrently causes unnecessary
synchronization overhead:
public class myClass
{
private static
myObject1 myObj1;
private static
mutex1 = new Object();
private static
myObject2 myObj2;
private static
mutex2 = new Object();
...
public static void
updateObject1()
{
synchronized(mutex1)
{
// update
myObj1 ...
}
}
public static void
updateObject2()
{
synchronized(mutex2)
{
// update
myObj2 ...
}
}
...
}
Use Private Fields
Making fields private protects them from unsynchronized
access. Controlling their access means these fields need to be synchronized
only in the class's critical sections when they are being modified.
Use a Thread Safe Wrapper
Provide a thread-safe wrapper on objects that are not
thread-safe. This is the approach used by the collection interfaces in JDK 1.2.
Use Immutable Objects
An immutable object is one whose state cannot be changed
once it is created. Since there is no method that can change the state of any
of the object's instance variables once the object is created, there is no need
to synchronize on any of the object's methods.
This approach works well for objects, which are small and
contain simple data types. The disadvantage is that whenever you need a
modified object, a new object has to be created. This may result in creating a
lot of small and short-lived objects that have to be garbage collected. One
alternative when using an immutable object is to also create a mutable wrapper
similar to the thread-safe wrapper.
An example is the String and StringBuffer class in Java. The
String class is immutable while its companion class StringBuffer is not. This
is part of the reason why many Java performance books recommend using
StringBuffer instead of string concatenation.
Know Which Java Objects Already Have
Synchronization Built-in
Some Java objects (such as Hashtable, Vector, and
StringBuffer) already have synchronization built into many of their APIs. They
may not require additional synchronization.
Do Not Under-Synchronize
Some Java variables and operations are not atomic. If these
variables or operations can be used by multiple threads, you must use
synchronization to prevent data corruption. For example: (i) Java types long
and double are comprised of eight bytes; any access to these fields must be
synchronized. (ii) Operations such as ++ and -- must be synchronized because
they represent a read and a write, not an atomic operation.
Monitor Synchronization
Java synchronization can cause a deadlock. The best way to
avoid this problem is to avoid the use of Java synchronization. One of the most
common uses of synchronization is to implement pooling of serially reusable
objects. Often, you can simply add a serially reusable object to an existing
pooled object. For example, you can add Java Database Connectivity (JDBC) and
Statement object to the instance variables of a single thread model servlet, or
you can use the Oracle JDBC connection pool rather than implement your own
synchronized pool of connections and statements.
If you must use synchronization, you should either avoid
deadlock, or detect it and break it. Both strategies require code changes. So,
neither can be completely effective because some system code uses
synchronization and cannot be changed by the application.
To prevent deadlock, simply number the objects that you must
lock, and ensure that clients lock objects in the same order.
Proprietary JVM extensions may be available to help spot
deadlocks without having to instrument code, but there are no standard JVM
facilities for detecting deadlock.
Monitor and Fix Resource Leaks
One way to fix resource leaks is straightforward - a
periodic restart. It provides good protection against slow resource leaks. But
it is also important to spot applications that are draining resources too
quickly, so that any software bugs can be fixed. Leaks that prevent continuous
server operation for at least 24 hours must be fixed in the application code,
not by application restart.
Common programming mistakes are:
Not returning the resource to the pool (or not removing it
from the pool) after handling an error.
Relying on the garbage collector to invoke finalize() and
free resources. Never rely on the garbage collector to manage any resource
other than memory.
Not discarding old object references which prevent recycling
the memory occupied by the objects.
Monitoring resource usage should be a combination of code
instrumentation and external monitoring utilities. With code instrumentation,
calls to an application-provided interface, or calls to a system-provided
interface like Oracle Dynamic Monitoring System (DMS), are inserted at key
points in the application's resource usage lifecycle. Done correctly, this can
give the most accurate picture of resource use. Unfortunately, the same
programming errors that cause resource leaks are also likely to cause
monitoring errors. That is, you may forget to release the resource, or forget
to monitor the release of the resource.
Operating system commands like vmstat or ps in UNIX, provide
process-level information such as the amount of memory allocated, the number
and state of threads, or number of network connections. They can be used to
detect a resource leak. Some commercially available development tools can also
be used to find the leak.
In addition to compromising availability, resource leaks and
overuse decrease performance.
Always Use a Finally Clause in Each
Method to Cleanup
In Java, it is impossible to leave the try or catch blocks
(even with a throw or return statement) without executing the finally block. If
for any reason the instance variables cannot be cleaned, throw a catch-all
exception that should cause the caller to discard its object reference to this
now corrupt object. If, for any reason the static variables cannot be cleaned,
throw an InternalError or equivalent that will ultimately result in restarting
the now corrupt JVM.
Discard Objects That Throw Catch-All
Exceptions
In many cases, these exceptions indicate that the internal
state of the invoked object is corrupt, and that further invocations will also
fail. Keep the object reference only if careful scrutiny of the exception shows
it is benign, and further invocations on this object are likely to succeed.
Adopt a guilty unless proven innocent approach. For example,
a SQLException thrown from an Oracle JDBC invocation could represent one of
thousands of error conditions in the JDBC driver, the network, or the database
server. Some of these errors (for example, subclass SQLWarning) are benign.
Some SQLExceptions (for example, "ORA-3113: end of file on communication
channel") definitely leave the JDBC object useless. Most SQLExceptions do
not clearly specify what state the JDBC object is left in. The best approach is
to enumerate the benign error codes that could occur frequently in your
application and can definitely be retried, such as a unique key violation for user-supplied
input data. If any other error code is found, discard the potentially corrupt
object that threw the exception.
Discard all object references to the (potentially) corrupt
object. Be sure to remove the corrupt object from all pools in order to prevent
pools from being poisoned by corrupt objects. Do not invoke the corrupt object
again - instantiate a brandnew object instead.
When you are sure that the corrupt objects have been
discarded and that the catching object is not corrupt, throw a non-catch-all
exception so that the caller does not discard this object.
Design Transactions Usage Correctly
Transactions should not span client requests because this
can tie up shared resources indefinitely.
Requests generally should not span more than one transaction,
because a failure in mid-request could leave some transactions committed and
others rolled back. If this requires application-level compensation to recover,
then availability or data integrity may be compromised.
Transactions generally should not span more than one
database, because distributed transactions lock shared resources longer, and
failure recovery may require simultaneous availability and coordination of
multiple databases.
Applications that require a single client request (for
example, a confirm checkout request in a shopping cart application) to
ultimately affect several databases (for example, credit card, fulfillment,
shopping cart, and customer history databases) should perform the first step
with one database, and in the same transaction queue a message in the first
database addressed to the second database. The second database will perform the
second step and queue the third step, and so on. This queued transaction chain
will eventually complete automatically, or an administrator will see an
undeliverable message and will have to manually compensate.
Put Business Logic in the Right Place
In general, you should not implement business logic in your
client program. Instead, put validation and defaulting logic in your entity
objects, and put client-callable methods in application modules, view objects,
and view rows.
Working with application module methods allows the client
program to encapsulate task-level custom code in a place that allows
data-intensive operations to be done completely in the middle-tier without
burdening the client.
Working with view object methods allows the client program
to access the entire row collection for cross-row calculations and operations.
Working with view row methods allows the client program to
operate on individual rows of data. There are three types of custom view row methods
you may want to create:
Accessor methods: The oracle.jbo.Row interface (which view
rows implement) contains the methods getAttribute() and setAttribute(), but
these methods are not typesafe. You can automatically generate custom typesafe
accessors when you generate a custom view row class.
Delegators to entity methods: By design, clients cannot
directly access entity objects. If you want to expose an entity method to the
client tier, you should create a delegator method in a view row.
Entity-independent calculations: This is useful if the
calculation uses attributes derived from multiple entity objects or from no
entity objects.
Avoid Common Errors That Can Result
In Memory Leaks
In Java, memory bugs often appear as performance problems,
because memory leaks usually cause performance degradation. Because Java
manages the memory automatically, developers do not control when and how
garbage is collected. To avoid memory leaks, check your applications to make
sure they:
Release JDBC ResultSet, Statement, or connection.
Release failures here are usually in error conditions. Use a
finally block to make sure these objects are released appropriately.
Release instance or resource objects that are stored in
static tables.
Perform clean up on serially reusable objects.
An example is appending error messages to a Vector defined
in a serially reusable object. The application never cleaned the Vector before
it was given to the next user. As the object was reused over and over again,
error messages accumulated, causing a memory leak that was difficult to track
down.
Avoid Creating Objects or Performing Operations
That May Not Be Used
This mistake occurs most commonly in tracing or logging code
that has a flag to turn the operation on or off during runtime. Some of this
code goes to great lengths creating and formatting output without checking the
flag first, creating many objects that are never used when the flag is off.
This mistake can be quite expensive, because tracing and logging usually
involves many String objects and operations to translate the message or even
access to the database to retrieve the full text of the message. Large numbers
of debug or trace statements in the code make matters worse.
Replace Hashtable and Vector With
Hashmap, ArrayList, or LinkedList If Possible
The Hashtable and Vector classes in Java are very powerful,
because they provide rich functions. Unfortunately, they can also be easily
misused. Since these classes are heavily synchronized even for read operations,
they can present some challenging problems in performance tuning. Hence, the
recommendations are:
Use an Array Instead of an ArrayList If the Size Can Be
Fixed
Use an ArrayList or LinkedList To Hold a List of Objects In
a Particular Sequence
Use HashMap or TreeMap To Hold Associated Pairs of Objects
Replace Hashtable, Vector, and Stack
Avoid Using String As the Hash Key (If Using JDK Prior to
1.2.2)
Use an Array Instead of an ArrayList If
the Size Can Be Fixed
If you can determine the number of elements, use an Array
instead of an ArrayList, because it is much faster. An Array also provides type
checking, so there is no need to cast the result when looking up an object.
Use an ArrayList or LinkedList To
Hold a List of Objects In a Particular Sequence
A List holds a sequence of objects in a particular order
based on some numerical indexes. It will be automatically resized. In general,
use an ArrayList if there are many random accesses. Use a LinkedList if there
are many insertions and deletions in the middle of the list.
Use HashMap or TreeMap To Hold
Associated Pairs of Objects
A Map is an associative array, which associates any one
object with another object. Use a HashMap if the objects do not need to be
stored in sorted order. Use TreeMap if the objects are to be in sorted order.
Since a TreeMap has to keep the objects in order, it is usually slower than a
HashMap.
Replace Hashtable, Vector, and Stack
Replace a Vector with an ArrayList or a LinkedList.
Replace a Stack with a LinkedList.
Replace a Hashtable with a HashMap or a TreeMap.
Vector, Stack, and Hashtable are synchronized-views of List
and Map. For example, you can create the equivalent of a Hashtable using:
private Map hashtable = Collections.synchronizedMap (new
HashMap());
However, bear in mind that even though methods in these
synchronized-views are thread-safe, iterations through these views are not
safe. Therefore, they must be protected by a synchronized block.
Reuse Objects Instead of Creating New
Ones If Possible
Object creation is an expensive operation in Java, with
impact on both performance and memory consumption. The cost varies depending on
the amount of initialization that needs to be performed when the object is to
be created. Here are ways to minimize excess object creation and garbage
collection overhead:
Use a Pool to Share Resource Objects
Recycle Objects
Use Lazy Initialization to Defer Creating the Object Until
You Need It.
Use a Pool to Share Resource Objects
Examples of resource objects are threads, JDBC connections,
sockets, and complex user-defined objects. They are expensive to create, and
pooling them reduces the overhead of repetitively creating and destroying them.
On the down side, using a pool means you must implement the code to manage it
and pay the overhead of synchronization when you get or remove objects from the
pool. But the overall performance gain you get from using a pool to manage
expensive resource objects outweighs that overhead.
However, be cautious on implementing a resource pool. The
following mistakes in pool management are often observed:
a resource object which should be used only serially is
given to more than one user at the same time objects that are returned to the
pool are not properly accounted for and are therefore not reused, wasting
resources and causing a memory leak elements or object references kept in the
pool are not reset or cleaned up properly before being given to the next user
These mistakes can have severe consequences including data
corruption, memory leaks, a race condition, or even a security problem. Our
advice in managing your pool is: keep your algorithm simple.
The J2EE section in this document includes examples showing
how you can use Oracle's built-in JDBC connection caching and the servlet's
SingleThreadModel to help manage a shared pool without implementing it
yourself.
Recycle Objects
Recycling objects is similar to creating an object pool. But
there is no need to manage it because the pool only has one object. This
approach is most useful for relatively large container objects (such as Vector
or Hashtable) that you want to use for holding some temporary data. Reusing
these objects instead of creating new ones each time can avoid memory
allocation and reduce garbage collection.
Similar to using a pool, you must take precautions to clear
all the elements in any recycled object before you reuse it to avoid memory
leak. The collection interfaces have the built-in clear() method that you can
use. If you are building your object, you should remember to include a reset()
or clear() method if necessary.
Use Lazy Initialization to Defer
Creating the Object Until You Need It.
Defer creating an object until it is needed if the
initialization of the object is expensive or if the object is needed only under
some specific condition.
public class myClass
{
private
mySpecialObject myObj;
...
public
mySpecialObject
getSpecialObject()
{
if (myObj ==
null)
myObj =
new mySpecialObject();
return myObj;
}
...
}
Use Stringbuffer Instead of String
Concatenation
The String class is the most commonly used class in Java.
Especially in Web applications, it is used extensively to generate and format
HTML content.
String is designed to be immutable; in order to modify a
String, you have to create a new String object. Therefore, string concatenation
can result in creating many intermediate String objects before the final String
can be constructed. StringBuffer is the mutable companion class of String; it
allows you to modify the String. Therefore, StringBuffer is generally more
efficient than String when concatenation is needed.
This section also features the following practices:
Use StringBuffer Instead of String Concatenation If You
Repeatedly Append to a String In Multiple Statements
Use Either String or StringBuffer If the Concatenation Is
Within One Statement
Use StringBuffer Instead of String Concatenation If You Know
the Size of the String
Use StringBuffer Instead of String
Concatenation If You Repeatedly Append to a String In Multiple Statements
Using the "+=" operation on a String repeatedly is
expensive.
For example:
String s = new
String();
[do some work
...]
s += s1;
[do some more
work...]
s += s2;
Replace the above string concatenation with a StringBuffer:
StringBuffer strbuf = new StringBuffer();
[do some work
...]
strbuf.append(s1);
[so some more
work ...]
strbuf.append(s2);
String s = strbuf.toString();
Use Either String or StringBuffer If
the Concatenation Is Within One Statement
String and StringBuffer perform the same in some cases; so
you do not need to use StringBuffer directly.
String s =
"a" + "b" + "c";
to
String s =
"abc";
Optimization is done automatically by the compiler.
The Java2 compiler will automatically collapse the above.
The Java2 compiler will also automatically convert the
following:
String s = s1
+ s2;
to
String s =
(new StringBuffer()).append(s1).append(s2).toString();
In these cases, there is no need to use StringBuffer
directly.
Use StringBuffer Instead of String
Concatenation If You Know the Size of the String
The default character buffer for StringBuffer is 16. When
the buffer is full, a new one has to be re-allocated (usually at twice the size
of the original one). The old buffer will be released after the content is
copied to the new one. This constant reallocation can be avoided if the
StringBuffer is created with a buffer size that is big enough to hold the
String.
The following will be more efficient than using a String
concatenation.
String s = (new
StringBuffer(1024)).
append(s1).append(s2). toString();
will be faster than
String s = s1 +
s2;
Loose Coupling
Avoid using implementation types (i.e., HashSet, ArrayList);
use the interface (i.e, Set, List) instead.
Here's an example of code that would trigger this rule:
import java.util.*;
public class Bar {
// Use List instead
private ArrayList
list = new ArrayList();
// Use Set instead
// public Set getFoo() {…
public HashSet
getFoo() {
returnnew HashSet();
}
}
Only One Return
A method should have only one exit point, and that should be
the last statement in the method.
Here's an example of code that would trigger this rule:
public class
OneReturnOnly1 {
public void foo(int
x) {
if (x > 0) {
return
"hey"; // oops, multiple exit
points!
}
return
"hi";
}
}
Null Assignment
Assigning a "null" to a variable (outside of its
declaration) is usually bad form. Sometimes, the assignment is an indication
that the programmer doesn't completely understand what is going on in the code.
NOTE: This sort of assignment may in rare cases be useful to
encourage garbage collection. If that's what you're using it for, by all means,
disregard this rule :-)
Here's an example of code that would trigger this rule:
public class Foo {
public void bar() {
Object x = null;
// This is OK.
x = new Object();
// Big, complex
piece of code here.
x = null; // This
is BAD.
// Big, complex
piece of code here.
}
}
Call Super in Constructor
It is a good practice to call super() in a constructor. If
super() is not called but another constructor (such as an overloaded
constructor) is called, this rule will not report it.
Here's an example of code that would trigger this rule:
public class Foo extends Bar{
public Foo() {
// call the
constructor of Bar
super();
}
public Foo(int code)
{
// do something with
code
this();
// no problem with
this
}
}
Switch Statements Should Have Default
Switch statements should have a default label.
Here's an example of code that would trigger this rule:
public class Foo {
public void bar() {
int x = 2;
switch (x) {
case 2: int j = 8;
}
}
}
Avoid Reassigning Parameters
Reassigning values to parameters is a questionable practice.
Use a temporary local variable instead or use only when required.
Here's an example of code that would trigger this rule:
public class Foo {
private void
foo(String bar) {
bar =
"something else";
}
}
Final Field Could Be Static
If a final field is assigned to a compile-time constant, it
could be made static, thus saving overhead in each object at runtime.
Here's an example of code that would trigger this rule:
public class Foo {
public final int BAR
= 42; // this could be static and save some space
}
Compare Objects With Equals
Use equals() to compare object references; avoid comparing
them with ==.
Here's an example of code that would violate this rule:
class Foo {
boolean bar(String a,
String b) {
return a == b;
}
}
If you have to compare POJO objects, override the equals and
hashcode, most IDE’s can do this for you, this will be faster and less verbose
than you compare field by field.
Position Literals First In
Comparisons if you don’t want to check for null first
Position literals first in String comparisons - that way if
the String is null you won't get a NullPointerException, it'll just return
false.
Here's an example of code that would violatethis rule:
class Foo {
boolean bar(String x)
{
return
x.equals("2"); // should be "2".equals(x)
}
}
Close Resource
Ensure that resources (like Connection, Statement, and
ResultSet objects) are always closed after use.
Here's an example of code that would violate this rule:
public class Bar {
public void foo() {
Connection c =
pool.getConnection();
try {
// do stuff
} catch
(SQLException ex) {
// handle
exception
} finally {
// oops, should
close the connection using 'close'!
// c.close();
}
}
}
Non Static Initializer
A nonstatic initializer block will be called any time a
constructor is invoked (just prior to invoking the constructor). While this is
a valid language construct, it is rarely used and is confusing.
Here's an example of code that would violate this rule:
public class MyClass {
// this block gets
run before any call to a constructor
{
System.out.println("I am about to construct myself");
}
}
Avoid Protected Field in Final Class
Do not use protected fields in final classes since they
cannot be subclassed. Clarify your intent by using private or package access
modifiers instead.
Here's an example of code that would violate this rule:
public final class Bar {
private int x;
protected int y; // Bar cannot be subclassed, so is y really
// private or package visible???
Bar() {}
}
Avoid Synchronized At Method Level
Method level synchronization can backfire when new code is
added to the method. Block-level synchronization helps to ensure that only the
code that needs synchronization gets it.
Here's an example of code that would violate this rule:
public class Foo {
// Try to avoid this
synchronized void
foo() {
}
// Prefer this:
void bar() {
synchronized(this) {
}
}
}
Use Notify All Instead Of Notify
Thread.notify() awakens a thread monitoring the object. If
more than one thread is monitoring, then only one is chosen. The thread chosen
is arbitrary; thus it's usually safer to call notifyAll() instead.
Here's an example of code that would violate this rule:
public class Foo {
void bar() {
x.notify();
// If many threads
are monitoring x,
// only one (and you
won't know which) will be notified.
// use instead:
x.notifyAll();
}
}
Avoid Instantiating Objects in Loops
Detects when a new object is created inside a loop
Here's an example of code that would violate this rule:
public class Something {
public static void
main( String as[] ) {
for (int i = 0; i
>; 10; i++) {
Foo f = new
Foo(); //Avoid this whenever you can it's really
//expensive
}
}
}
String Instantiation
Avoid instantiating String objects; this is usually
unnecessary.
Here's an example of code that would violate this rule:
public class Foo {
private String bar =
new String("bar"); // just do a String bar = "bar";
}
For extract the max of JVM Garbage
Collector (GC) and memory management
Avoid at all costs, leave objects that aren’t marked as
final without be cleaned (set it to null) from memory, even within a method as
GC doesn’t actually run just because the method has ran, those method objects
will be in the object pool (the heap) up to the GC runs that can be hours after
or even in the next day everything depends on the heaven size (memory that the
JVM has allocated as max to be used), this also helps to avoid memory leaks. The main problem with Java that demands
constants Application Servers restarts is bad memory that’s caused by bad code.
Fragmented memory is a direct effect from bad code as well and that’s ends up
with Out Of Memory Errors as for the JVM load up more objects it needs
continuous memory. Meaning if you have 10Mb of memory free doesn’t mean that its
continuous memory addresses. The GC doesn’t see it and that’s the reason good
code matters. The JVM just can load up objects to the memory if the free memory
is compose by continuous memory address.
E.g.:
Amount of free memory: 10240kb
(or 10Mb);
Max free memory continuous available: 800kb;
This imply that the largest object that the JVM may load up
is 800kb, if the object has 800.1kb it will throw Out Of Memory.
The reason is that the JVM doesn’t need run GC as it has
10Mb and the object been load has 800Kb, so no GC in this operation, however
when the class loader try to loads up the object into the memory and the object
is bigger than the max continuous free memory, it will crash without run the
GC.
Marking the object to final or setting it as null after use,
will mark the object for be GC’ed.
E.g.:
final List<AnObject> objList = foo.getMyList();
or
List<AnObject> objList = null;
…doing some tasks that will update the objList
final An
objList = null;
Avoid return values that doesn’t have a memory reference as
it will live longer in memory even after several GC ran and also this creates
other problem that is make the GC quite slower as it need to check all the
references taking longer than check just one.
E.g.:
public List<AnObject> getObjList() {
…..
// don’t
return getList();
// do
final List<AnObject> objToReutn = getList();
return objToReturn;
}
Maximize the object reuse as creation ‘costs’ more for JVM
(mean takes longer to create then reuse what’s in there).
Use primitive types everywhere when it’s possible.
Avoid to load long strings in memory, use stream instead and
always that’s possible.
Avoid add up too many jars in the project for use a small
fraction of it to resolve the problem you can extract just what you need from
the jar creating other jar or create the part of code needed and don’t using at
all the third part jar. Keep in mind that “Frameworks aren’t in most cases
written by experts, so in over 90% of cases it’s not optimized for optimal
memory use”
Use the right type for the right data, e.g.:
// don’t
String testVar = “C”;
Double value = 10.00;
// do
char testVar = ‘C’;
int value = 10;
// or if you need floating points
float value = 10.0f;
Use ENUMS instead having larger or several constant files whenever possible, as
it’s has several advantages over those several or immense constants classes. The
main advantage is speed and very low memory footprint as the class loader will
be loading up just a small set and getting just one object value and then
destroying the object, as if loading up a class full of constants will consume
far more memory.
Reference used: