9 Performance Tuning Tips in Java

9 Performance Tuning Tips in Java:

Before start doing performance tuning/optimizations, first use any profiler to identify the areas for optimizations using profilers

Few Java Profilers:

yourkit
jProfiler
Java Flight Recorder
Eclipse Test & Performance Tools Platform Project

1. Use available data structures or algorithms

If you want to convert list to set or vice versa, instead of iterating a list/set to copy the values to list/set, you can use the inbuilt method addAll().

So always check for an inbuilt functionality before writing your own code to achieve your functionality.

2. Logger Vs System.out.println()

Logger: to print the output/warning/errors/debug texts in the file.

Sysout: prints everything in the console.

Logger is faster than sysout and also has an option to configure for different levels. So for better performance remove all the sysouts and put loggers wherever it is needed and applicable.

3. Primitive vs Wrapper Class ?

Assigning a number 5 to int (primitive data type) and Integer (wrapper classes) will have different performance impacts. It is highly recommended to assign the number to int(primitive data type) only until and unless you really need to perform some additional operations on the same.

Primitive data types are simple and faster.

Wrapper classes can be used when you want to do any extra functionalities(like converting integer to string), then Integer wrapper class can be preferred. Also remember collections will allow wrapper or boxed primitives only.

Note: As per the profiling it’s not really creating much performance impact, but still it can also be considered for performance tuning.

3.1 Try to use best primitive type possible

If suppose you need to store only the values which is a maximum of 100, then instead of using int or long, we can use byte. because for byte datatype it allocates only 8 bits (1 byte), so the memory utilization can be saved to the greater level.

3.1 Use Always int for all types of your number operations

I can tell one another controversy statement here, nowadays we don’t really have any memory problems as all the latest devices are having very good memories. So we can always go ahead with int ( as, this is the default one which Java uses internally for numerical operations ).

Because we may forget to typecast to the particular type after our operation.

byte b = 100/2;

What do you think about the above line ? Will this creates any exceptions ? Yes, it creates. because 100/2 = 50, which can be assigned to byte datatype. but java default uses int so it throws the exception like int can’t be assigned to byte.

byte b = (Byte) 100/2;

So additional casting requires if you are planning to utilize the memories in a more effective way, by using right datatypes for the numbers you are using.

4. String vs StringBuilder

whenever a string concatenations need to be done use mutable objects such as stringbuilder/stringbuffer rather than + concatenation operator.

Use Mutable objects:

Instead of creating multiple objects all the time(String calls empty constructor all the time and creates a new object), if you use mutable objects it will not create new objects all the time, it will change the same object, which help us to save the time.

Since in string new string need to be created on each appending/concatinating, where as in stringbuilder/StringBuilder we can change directly in that itself. So it’s highly recommended to use StringBuilder on appending/concating with in the loops.

For eg:

4.1 Loop Appending Using String: If you execute this loop, it takes around 200 milli seconds:

String s = "0";
for(int i = 1;i<10000;i++){
s = s+i;
}

4.2 Loop Appending Using StringBuilder/StringBuffer: It just takes less than 5 milli seconds.

StringBuilder s = new StringBuilder("0");
for (int i = 1; i < 10000; i++) {
s.append(i);
}

Remember: String immutability really provides lot of advantages over mutablity but it fails to perform only when appending/concatenating within the loop.

Note: If String used with the objects containing lots of data, memory consumption can become an issue.

5. Avoid synchronization when not needed

StringBuilder vs StringBuffer:

5.1 StringBuffer: It takes min 23 milli seconds

StringBuffer s = new StringBuffer("0");
for (int i = 1; i < 1000000; i++) {
s.append("");
}

5.2 StringBuilder: It takes max of 10 milli seconds

StringBuilder s = new StringBuilder("0");
for (int i = 1; i < 1000000; i++) {
s.append("");
}

Both executes same number of time, since stringbuffer is thread safe, it takes extra time to complete it’s execution on synchronization.

6. Prefer for loop/for each/while loop rather than iterator

Since iterator is creating iterator newinstance(), for loopings always prefer for each/for loops/while loops.

Loop performance testing by mkyong

7. Assign once and use multiple times/places

Change the below code block appropriately:

Before refactoring:

if (getMobilesList() != null) {
for (Mobiles mobile : getMobilesList()) {
// business logics
}
}

Here getMobilesList() is used for both null check and for for each iteration. If this method has very simple logics/implementations then performance will be unnoticable/ignorable.

If getMobilesList() has very complex logics then calling it in two places will execute the same complext logics twice and affect the performance surely.

That case, refactoring the above code like below will really help for good performance,

After refactoring:

List mobilesLst = getMobilesList();

if (mobilesLst != null) {
for (Mobiles mobile : mobilesLst ) {
 // business logics
}
}

Here getMobilesList() executed only once and the same is referenced with mobilesLst. So using mobilesLst twice/thrice/more than that will not have any performance impact, since it executed only once.

8. Consider Best Big(O) Notation Algorithms or logics

This is the one which needs to be understood very deeply to implement properly, to explain you I will take a below scenario:

Now you have a list with 1,00,000 sorted elements and need to write a searching algorithm to get the given element position, now you can either write linear searching algorithm or binary searching algorithm for this scenario.

But linear has the big o notation of O(n) and binary has the big o notation of just O(log n) which means for 100 elements linear searching algorithm may take 100 iterations at worst case, where as binary searching algorithm takes only 6 to 7 iterations even in worst case for any of the given element to be searched.

Binary Search Implementation in Java Example
All Algorithms with Java Example:

Few mostly required and used performance tunings are shared above. You can go through the complete java performance tuning tips in the below link,

Java Performance Guidelines from IBM

Note: Always keep upgrading your jdk. Because lot of performance improvements will be included in the upgraded version.

9. Use Streams Effectively in Java 8 & Above.

Implementation of functional programming through streams & lambda expressions really improves the performance of the java application to the greater levels.

If suppose you have a list and need to iterate and perform some operations, then through collections, you may need to allocate some spaces to get it done.

Where as in functional programming, it just performs the data transformation through streams and your tasks get done without allocating any actual space in your memory, this is the key idea to save memory and improve your performance by using streams effectively in Java 8.

Leave a Reply