30 Essential Java Performance Tips Every Developer Should Know
This article presents a comprehensive list of practical Java performance optimization techniques—from proper use of singletons and avoiding static variables to efficient collection handling, string manipulation, and resource management—aimed at helping developers write faster, more memory‑efficient code.
In Java programs, most performance issues stem from the code itself rather than the language; cultivating good coding habits can significantly improve program performance.
1. Use singletons where appropriate
Singletons can reduce loading overhead and improve efficiency, but they are suitable only for controlling resource usage, limiting instance creation, and enabling data sharing across threads or processes.
2. Avoid arbitrary static variables
Objects referenced by static variables are not reclaimed by the garbage collector, causing them to live as long as the class does.
public class A {
private static B b = new B();
}3. Minimize frequent object creation
Creating objects inside frequently called methods or loops incurs both allocation and garbage‑collection costs; reuse objects or replace them with primitive types or arrays when possible.
4. Prefer the final modifier
Final classes and methods cannot be overridden, allowing the compiler to inline them and potentially boost performance by up to 50%.
Example of making a setter final:
class MAF {
public void setSize(int size) {
_size = size;
}
private int _size;
}
// corrected version
class DAF_fixed {
final public void setSize(int size) {
_size = size;
}
private int _size;
}5. Use local variables
Method parameters and temporary variables are stored on the fast stack, whereas static or instance variables reside on the slower heap.
6. Choose between wrapper and primitive types wisely
Wrappers are objects allocated on the heap; primitives are stored on the stack. Use wrappers only when collections require them.
7. Use synchronized sparingly and keep synchronized methods small
Synchronization incurs high overhead and can cause deadlocks; prefer method‑level synchronization over block‑level when possible.
8. Avoid finalize()
Finalizers increase GC workload and can pause the application, especially during young‑generation collection.
9. Prefer primitive types over objects
Creating a new String object allocates both the object and an internal char[] array. String str = "hello"; vs.
String str = new String("hello");10. In single‑threaded contexts, prefer HashMap and ArrayList
Classes like Hashtable and Vector use built‑in synchronization, reducing performance.
11. Create HashMap with an appropriate initial capacity
Specifying capacity and load factor avoids repeated rehashing and resizing.
public HashMap(int initialCapacity, float loadFactor);12. Reduce repeated calculations in loops
Cache loop‑invariant values outside the loop.
for(int i=0, len=list.size(); i<len; i++) {
// loop body
}13. Avoid unnecessary object creation
Instantiate objects only inside the conditional block where they are needed.
// less efficient
A a = new A();
if(i==1){
list.add(a);
}
// better
if(i==1){
A a = new A();
list.add(a);
}14. Release resources in finally blocks
Ensuring resources are closed in finally guarantees cleanup regardless of execution outcome.
15. Replace division with right‑shift operations
Shifts are faster than division for powers of two.
int num = a >> 2; // instead of a / 4
int num = a >> 3; // instead of a / 816. Replace multiplication with left‑shift operations
Shifts are faster than multiplication for powers of two.
int num = a << 2; // instead of a * 4
int num = a << 3; // instead of a * 817. Pre‑size StringBuffer
Specify an initial capacity to avoid costly automatic resizing.
StringBuffer buffer = new StringBuffer(1000);18. Nullify references only when necessary
Local references are reclaimed automatically when a method exits; explicit null is rarely needed.
19. Avoid large two‑dimensional arrays
They consume significantly more memory than one‑dimensional arrays.
20. Use split() sparingly
Regular‑expression based splitting is expensive; consider alternatives like StringUtils.split() for high‑frequency calls.
21. Choose between ArrayList and LinkedList wisely
Use ArrayList for fast random access; LinkedList excels at frequent insertions/removals.
22. Prefer System.arraycopy() over manual loops
System.arraycopy()is much faster for copying arrays.
23. Cache frequently used objects
Use arrays, HashMap, or third‑party caches (e.g., EhCache, OSCache) while being mindful of memory consumption.
24. Avoid allocating very large memory blocks
Large contiguous allocations may fail as the heap becomes fragmented.
25. Use exceptions judiciously
Creating an exception captures a full stack trace, which is costly; only throw when truly exceptional.
26. Reuse objects, especially String instances
Prefer StringBuilder or StringBuffer for concatenation to reduce temporary objects.
27. Do not re‑initialize variables unnecessarily
Default constructors already set fields to default values; redundant initialization adds overhead.
28. Write SQL keywords in uppercase
Uppercase SQL in Java‑Oracle code reduces parser workload.
29. Release I/O and database resources promptly
Close connections and streams as soon as they are no longer needed to avoid heavy overhead.
30. Excessive object creation can cause memory leaks
Manually nullify references only when the object’s lifetime extends beyond the method scope.
31. Prefer method‑level synchronization over block‑level
Method synchronization reduces the amount of code holding the lock.
32. Place try/catch outside loops
Wrapping the entire loop avoids repeated exception‑handling overhead.
33. Set StringBuffer initial capacity to improve performance
Default capacity is 16; exceeding it triggers costly resizing.
34. Use java.util.Vector cautiously
Vector expands by doubling its capacity, incurring similar overhead to StringBuffer.
35. Create objects without new when possible
Cloning an existing instance avoids invoking constructors.
public static Credit getNewCredit(){
return new Credit();
}
// improved version
private static Credit BaseCredit = new Credit();
public static Credit getNewCredit(){
return (Credit)BaseCredit.clone();
}36. Iterate HashMap efficiently
Map<String, String[]> paraMap = new HashMap<>();
for (Entry<String, String[]> entry : paraMap.entrySet()) {
String key = entry.getKey();
String[] values = entry.getValue();
}37. Array vs. ArrayList
Arrays offer maximum speed but fixed size; ArrayList provides dynamic resizing at a performance cost.
38. In single‑threaded code, prefer HashMap and ArrayList
Avoid synchronized collections like Hashtable and Vector unless necessary.
39. StringBuffer vs. StringBuilder
StringBufferis thread‑safe; StringBuilder is faster but not safe for concurrent use. Specify capacity for both when possible.
40. Consider static methods when instance state is not needed
Static methods are invoked faster and clarify that they do not modify object state.
The above tips cover a range of Java performance optimizations concerning time, memory, and code structure; they should be applied judiciously based on the specific context.
Java Interview Crash Guide
Dedicated to sharing Java interview Q&A; follow and reply "java" to receive a free premium Java interview guide.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
