11 simple Java performance tuning techniques, java Tuning
Most developers naturally think that performance optimization is complex and requires a lot of experience and knowledge. Well, it cannot be said that this is completely wrong. Optimizing applications to achieve optimal performance is not easy. However, this does not mean that you cannot do anything without such knowledge. Here are 11 easy-to-follow suggestions and best practices to help you create an application with good performance.
Most of the suggestions are for Java. However, there are also several suggestions that are language-independent and can be applied to all applications and programming languages. Before discussing performance tuning techniques for Java, let's take a look at general skills.
1. Do not optimize it before you know it is necessary
This may be one of the most important performance tuning techniques. You should follow common best practices and try to implement use cases efficiently. However, this does not mean that you should replace any standard library or build complex optimizations before you prove necessary.
In most cases, premature optimization not only takes a lot of time, but also makes the code hard to read and maintain. What's worse, these optimizations usually do not bring any benefit, because you spend a lot of time optimizing the non-critical part of the application.
So how do you prove that you need to optimize something?
First, you need to define how fast the application code is. For example, you need to specify the maximum response time for all API calls or the number of records to be imported within a specific time range. After completing this, you can measure which parts of the application are too slow to be improved. Next, let's look at the second technique.
2. Use analyzer to find the real bottleneck
After you have followed the first suggestion and determined that some parts of the application need to be improved, where can you start?
You can solve the problem in two ways:
- Check your code and start with what looks suspicious or what you think may cause problems.
- You can also use the analyzer to obtain detailed information about the behavior and performance of each part of the code.
Hopefully I don't need to explain why the second method should always be followed.
Obviously, analyzer-based methods allow you to better understand the performance impact of code and focus on the most critical part. If you have used analyzer before, you must remember how surprised you were to find out which parts of the Code produced performance problems. To be honest, my first guess has led me to go wrong more than once.
3. Create a performance test suite for the entire application
This is another common technique that helps you avoid many unexpected problems that often occur after deploying performance improvement to production. You should always define a performance test suite to test the entire application and run it before and after performance improvement.
These additional test runs will help you identify the changed features and performance side effects, and ensure that there will be no updates that cause more disadvantages than benefits. This is especially important if you are working on components used by different parts of the application, such as databases or caches.
4. Handle the biggest bottleneck first
After creating a test suite and using analyzer to analyze the application, you can list a series of issues that need to be addressed to improve performance. This is good, but it still cannot answer where you should start. You can focus on quick-acting solutions or start with the most important issues.
Quick-acting solutions may be attractive at the beginning, because you can quickly show the first result. However, sometimes you may need to persuade other team members or management personnel to think performance analysis is worthwhile-because the effect is temporarily unavailable.
But in general, I suggest you first handle the most important performance issues. This will provide you with the greatest performance improvement, and may no longer need to solve some of the problems to meet performance requirements.
This is the end of common performance tuning techniques. Next let's take a closer look at some Java-specific skills.
5. Use StringBuilder to connect to String programmatically
There are many different options to connect to the String in Java. For example, you can use simple ++ or ++ =, and StringBuffer or StringBuilder.
So what method should you choose?
The answer depends on the code that connects to the String. If you add new content to the String programmatically, for example, in the for loop, you should use StringBuilder. It is easy to use and provides better performance than StringBuffer. However, remember that, compared with StringBuffer, StringBuilder is NOT thread-safe and may not be suitable for all use cases.
You only need to instantiate a new StringBuilder and call the append method to add a new part to the String. After you add all the parts, you can call the toString () method to retrieve the connected String.
The following code snippet shows a simple example. During each iteration, this loop converts I to a String and adds it with a space to StringBuilder sb. Therefore, the code will write "This is a test0 1 2 3 4 5 6 7 8 9" in the log file ".
StringBuilder sb = new StringBuilder(“This is a test”);for (int i=0; i<10; i++) { sb.append(i); sb.append(” “);}log.info(sb.toString());
As you can see in code snippets, you can provide the first element of String to the constructor. This will create a new StringBuilder, which contains the provided String and 16 additional characters. When you add more characters to StringBuilder, the JVM dynamically increases the size of StringBuilder.
If you already know how many characters your String will contain, you can provide the number to different constructor methods to instantiate the StringBuilder with the defined capacity. This further improves the efficiency because it does not need to dynamically expand its capacity.
6. Use + to connect the String in a statement
When you use Java to implement your first application, someone may have told you that you should not use ++ to connect to strings. If you are connecting strings in the application logic, this is correct. The String is immutable, and the connection results of each String are stored in a New String object. This requires additional memory, which slows down your application, especially if you connect multiple strings in a loop.
In these cases, you should follow Tip 5 and use StringBuilder.
However, if you just split the string into multiple lines to improve the readability of the Code, the situation is different.
Query q = em.createQuery(“SELECT a.id, a.firstName, a.lastName ”+ “FROM Author a ”+ “WHERE a.id = :id”);
In these cases, you should use a simple ++ to connect your string. The Java compiler will optimize this and execute connections during compilation. Therefore, at runtime, your code will use only one String, and you do not need to connect.
7. Try to use a primitive
Another simple and fast way to avoid any overhead and improve application performance is to use the basic type instead of its packaging class. Therefore, it is best to use int instead of Integer, and double instead of Double. This allows JVM to store values in the stack rather than in the heap to reduce memory consumption and perform more effective processing.
8. Try to avoid BigInteger and BigDecimal
Since we are discussing data types, let's take a quick look at BigInteger and BigDecimal. In particular, the latter is very popular for its accuracy. But there is a price.
BigInteger and BigDecimal require more memory than simple long or double, and will significantly slow down all calculations. Therefore, if you need extra precision or numbers that will exceed the long range, you 'd better think twice. This may be the only method you need to change to solve performance problems, especially when implementing mathematical algorithms.
9. Check the current log level first
This suggestion should be obvious, but unfortunately many programmers will ignore it when writing code. Check the current log level before creating debugging messages. Otherwise, you may create a log message string that will be ignored later.
Here are two negative examples.
// don’t do thislog.debug(“User [” + userName + “] called method X with [” + i + “]”);// or thislog.debug(String.format(“User [%s] called method X with [%d]”, userName, i));
In the above two cases, you will perform all the necessary steps to create a log message without knowing whether the log framework will use the log message. Therefore, you are advised to check the current log level before creating a debugging message.
// do thisif (log.isDebugEnabled()) { log.debug(“User [” + userName + “] called method X with [” + i + “]”);}
10. Use Apache Commons StringUtils. Replace instead of String. replace
In general, the String. replace method works normally and is very efficient, especially when Java 9 is used. However, if your application requires a lot of replacement operations and is not updated to the latest Java version, we still need to find faster and more effective alternatives.
The alternative answer is the StringUtils. replace method of Apache Commons Lang. As Lukas Eder described in his recent blog article, the StringUtils. replace method is far better than the String. replace method of Java 8.
And it only requires minor changes. Add Maven dependencies of the Apache Commons Lang project to the application pom. xml, and replace all calls of the String. replace method with the StringUtils. replace method.
// replace thistest.replace(“test”, “simple test”);// with thisStringUtils.replace(test, “test”, “simple test”);
11. cache expensive resources, such as database connections
Caching is a popular solution to avoid repeated execution of expensive or frequently used code segments. The general idea is simple: repeated use of these resources is cheaper than repeated creation of new resources.
A typical example is the database connection in the cache pool. It takes time to create a new connection. If you reuse an existing connection, this situation can be avoided.
You can also find other examples in the Java language itself. For example, the valueOf method of the Integer class caches values between-128 and 127. You may say that creating a new Integer is not too expensive, but because it is often used, the most common value in cache can also provide performance advantages.
However, when you consider caching, remember that the caching implementation will also produce overhead. You need to spend extra memory to store reusable resources. Therefore, you may need to manage the cache to make the resources accessible and delete outdated resources.
Therefore, before starting to cache any resources, make sure that the implementation of the cache is worthwhile, that is, you must use them more than enough.
Summary
As you can see, sometimes the performance of applications can be improved without too much work. Most of the suggestions in this article can be applied to your code only with a little effort.
However, the most important skills are those that have nothing to do with the programming language:
- Don't optimize it before you know it is necessary
- Use analyzer to find real bottlenecks
- First, handle the biggest bottleneck
Join the study exchange group 569772982..