Java Performance Optimization Tips: 34 Best Practices for Faster, Cleaner Code
This article presents 34 practical Java performance optimization guidelines—ranging from proper use of singletons and static variables to avoiding excessive object creation, leveraging final modifiers, preferring primitives, minimizing synchronization, and employing efficient collections—to help developers write faster, more memory‑efficient code.
Most performance issues in Java programs stem from the program itself rather than the language; adopting good coding habits can significantly improve performance.
1. Use singleton where appropriate – reduces loading overhead but not suitable everywhere. It controls resource usage, instance creation, and data sharing.
2. Avoid arbitrary static variables – static objects are not reclaimed by GC, leading to memory leaks.
public class A {
private static B b = new B();
}Static variable b lives as long as class A , persisting until program termination.
3. Avoid excessive object creation – creating objects inside frequently called methods or loops incurs creation and GC costs; reuse objects or use primitive types/arrays.
4. Use the final modifier – final classes and methods enable compiler inlining, improving performance up to 50% in some cases.
class MAF {
public void setSize(int size) { _size = size; }
private int _size;
}
class DAF_fixed {
final public void setSize(int size) { _size = size; }
private int _size;
}5. Prefer local variables – stack variables are faster than heap variables such as static or instance fields.
6. Choose between wrapper types and primitives wisely – primitives reside on the stack and are faster; use wrappers only when required by collections.
7. Minimize synchronized blocks – synchronization incurs high overhead and can cause deadlocks; keep synchronized methods small or replace with method‑level synchronization.
8. Do not use finalize() – finalizers increase GC workload and pause the application.
9. Prefer primitive types over objects
String str = "hello"; // uses string pool
String str = new String("hello"); // creates new object and char[]10. Use HashMap and ArrayList in single‑threaded contexts – HashMap/ArrayList are faster than synchronized HashTable/Vector.
11. Create HashMap with proper initial capacity
public HashMap(int initialCapacity, float loadFactor);12. Reduce repeated calculations in loops
for (int i = 0, len = list.size(); i < len; i++) { ... }13. Avoid unnecessary object creation
if (i == 1) {
A a = new A();
list.add(a);
}14. Release resources in finally blocks – ensures resources are closed regardless of execution outcome.
15‑16. Use bit‑shift instead of division/multiplication
int num = a >> 2; // a/4
int num = a << 3; // a*817. Set initial capacity for StringBuffer
StringBuffer buffer = new StringBuffer(1000);18. Nullify references only when necessary – usually local variables are reclaimed automatically.
19. Avoid large two‑dimensional arrays – they consume far more memory than one‑dimensional arrays.
20. Minimize use of split() – regex‑based split is costly; consider Apache StringUtils.split or cache results.
21. Choose between ArrayList and LinkedList based on access pattern
22. Use System.arraycopy() for bulk array copying
System.arraycopy(src, 0, dest, 0, length);23. Cache frequently used objects – use containers or third‑party caches like EhCache.
24. Avoid very large memory allocations – large contiguous blocks may be hard to find, leading to allocation failures.
25. Use exceptions sparingly – creating stack traces is expensive; catch exceptions around core logic instead of throwing frequently.
26. Reuse objects, especially Strings – prefer StringBuilder over String concatenation.
27. Do not re‑initialize variables unnecessarily
28. Follow SQL naming conventions – use uppercase for embedded SQL to reduce parser load.
29. Release database and I/O resources promptly
30. Prevent excessive object creation to avoid memory leaks
31‑32. Prefer method‑level synchronization and place try/catch outside loops
33‑34. Pre‑size StringBuffer/StringBuilder and Vector to avoid repeated resizing
35‑36. Use static methods when instance state is not needed and reuse objects via cloning
public static Credit getNewCredit() { return new Credit(); }
// or using a cached prototype
private static Credit baseCredit = new Credit();
public static Credit getNewCredit() { return (Credit) baseCredit.clone(); }37‑38. Prefer arrays for fixed‑size data and HashMap/ArrayList for single‑threaded use
39. StringBuffer vs StringBuilder – Buffer is thread‑safe; Builder is faster but not safe.
40. Use static methods when possible – they are faster and indicate no object state change.
These guidelines balance performance, readability, and maintainability; apply them according to the specific context of your Java application.
Architect
Professional architect sharing high‑quality architecture insights. Topics include high‑availability, high‑performance, high‑stability architectures, big data, machine learning, Java, system and distributed architecture, AI, and practical large‑scale architecture case studies. Open to ideas‑driven architects who enjoy sharing and learning.
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.