JAVA performance optimization: 35 small details to improve the efficiency of code operation (java interview)

1. Encourage the use of java8's functional development, considering its immutability characteristics.

Note: The operating efficiency of functional development on a multi-core server is positively related to the number of cores, while traditional java code does not have this feature.

2. It is recommended to use IDEA as a development tool and git as a version control tool.

Note: IDEA is currently the most powerful java development tool, and its efficiency, performance, and intelligence are currently top-notch. Developers need to overcome the incompatibility of changing from traditional eclipse, sts, myeclipse to idea.

3. IDEA installs the lombok plug-in, and adds lombok annotations to each entity class: @Data, @Builder,

@NoArgsConstructor,@AllArgsConstructor to simplify the code, you can directly add @Slf4j annotations to the classes that require log output for debugging, and you can directly call the log output in the code with http://log.xxx()

Note: The main function of lombok is to simplify the code. Some conventional methods can be implemented directly through lombok's annotations.

Lombok explained:

@Data Annotation: Annotation is on the class, directly implanting getter/setter methods, equals methods, toString methods, and hashCode methods for all properties of the class

@ NoArgsConstructor: Annotation is on the class, and no-argument construction method is implanted for the class

@AllArgsConstructor: Annotation is on the class, and a full-parameter construction method is implanted for the class

@ Builder: Annotation on the class, implants an internal class of Builder for the class, this class adopts the design pattern of the constructor, and the main function is to initialize the instance.

@Slf4j: Annotation is on the class, and the method for obtaining the slf4j log object is implanted for the class

According to the immutability of the function, when we initialize the instance, we are not allowed to use the new Objcet() method, nor to use the Class.newInstance() method. These initialized objects are too flexible and have minimal permissions. The principle of change is not allowed in the code.

In the above item 3, the @Builder annotation of lombok is added. We use the constructor of the class to initialize, and add val before the reference (this is also an annotation of lombok, but it is not necessary to use @, the function of this annotation is to compile When modifying the reference to final type, in line with immutability)

示例:val user = User.builder().age(18).username(“xxx”).build();

4. When declaring a variable, whether it is a member variable or a local variable, consider whether it can be declared as a final type (basically, it is possible, a few need to enter the code block, such as the loop body, try-catch is not possible), can If declared as final, use final modification. If it is not, consider whether there is a way to achieve it. If it is not, it can only be compromised.
Note: In addition to conforming to immutability, the final type also greatly improves the efficiency of the JVM (the addressing of the memory is very clear). A single item will not be effective, but a project will have thousands of variables, each of which is small. After the accumulative improvement, it will become a huge improvement.

5. Hierarchical description (important): I hope everyone can understand the DDD (Domain Driven Design) hierarchical design idea,
traditionally we all carry out MVC hierarchical, the M layer is the collective name of model, dao, and service, and the V layer is the view Layer, C is the control layer. Have you ever encountered this situation? When my Controller wants to call several services with transaction operations, there are two ways to do it: (1) Call multiple services directly in the controller for data Assemble returns to the view. (2) Encapsulate a large service, call dao multiple times to satisfy the business and then return it to the controller to call.

Analysis of the consequences of these two approaches: (1) The first approach, how to ensure the transaction problem, all are successful, we will consider it as a success, if one fails, we will consider this transaction as a failure, and the previous completion All should be rolled back, but the first approach obviously cannot guarantee this consistency problem, because our transaction control is in the business layer, and because of the spring framework we use, the controller layer cannot use spring's transaction proxy.

(2) The second approach is actually the mainstream approach of adopting MVC hierarchical design. Firstly, to ensure the transaction, and secondly to ensure the business, it is nothing more than an increase in the amount of code. So the question is, do you feel embarrassed about the status of this big service? In fact, we require fine-grained business, including transaction control, but also pay attention to atomicity, but we can analyze this business, it is not a business , But a "business" formed by the combination of multiple businesses. In fact, in the MVC hierarchical design, there is no so-called "business" status, but we compromise and treat it as a business.

The topic: DDD layered design ideas are roughly divided into 4 layers:

a: Infrastructure layer

——This layer contains various util classes, database operations, cache operations, queue operations and other basic elements of component operations, such as model and dao in MVC belong to this layer.

b: Domain layer (@Component)

——This layer contains fine-grained services. The infrastructure layer is called for business implementation. It is similar to the service layer, but is more granular than the service. It is basically just a simple CRUD operation in the service.

c: Application layer (@Service)

——This layer is the place where the "awkward business" mentioned in the case just now sits. Things to do at this layer: 1. The affairs are controlled at this layer. 2. Call the domain layer to assemble the data. The function of this layer is to encapsulate the complexity of the business, which can be directly invoked by the interface layer, in one step, writing logic in the interface layer is bad behavior, and the interface layer is open to the outside world, the simpler the better

d: Interface layer (@Controller/@RestController)

——The position of this layer is equivalent to the controller in MVC, open to the outside world, calling the application layer to return client data.

Through the above description, there should be a certain understanding. What I said is relatively simple and clear, and it is carried out by analogy with MVC. The true connotation of DDD thinking is hoped that everyone can delve into it, and if there is any insight, we can discuss it together and make progress together.

As can be seen from the above, the domain layer is the "fattest" in the entire system. This is why it is called DDD (Domain-Driven-Design). The normal operation of the entire system is driven by the domain and is designed in this way. The advantage lies in the scalability of the system, and the scalability improvement is huge.

Package naming convention: capital letters are not allowed in package naming. The reason is that git does not distinguish between case and java. In idea, java will consider different packages, but git will consider the same package and submit the code. , There will be problems when merging the code. Do not underline.

Code optimization is a very important subject. Some people may find it useless. What can be modified in some small areas? What impact does the modification have on the efficiency of the code? This is how I think about this question. Just like a whale in the ocean, is it useful to eat a small shrimp? It's useless, but after eating more shrimps, the whales are fed.

The same is true for code optimization. If the project is focused on going online as soon as possible without BUG, ​​then you can focus on the big ones at this time, and the details of the code need not be carefully polished; but if you have enough time to develop and maintain the code, you must consider each one at this time. The details that can be optimized, the accumulation of small optimization points, will definitely improve the operating efficiency of the code.

The goal of code optimization is

Reduce the size of the code

Improve the efficiency of code execution

Code optimization details

1. Try to specify the final modifier of the class and method

Classes with final modifiers are not derivable. In the Java core API, there are many examples of final applications, such as java.lang.String, the entire class is final. Specifying a final modifier for a class can prevent the class from being inherited, and specifying a final modifier for a method can prevent the method from being overridden. If a class is specified as final, all methods of that class are final. The Java compiler will look for opportunities to inline all final methods. Inlining has a significant effect on improving the efficiency of Java operations. For details, see Java runtime optimization. This can increase performance by an average of 50%.

2. Reuse objects as much as possible

Especially the use of String objects, StringBuilder/StringBuffer should be used instead when string connection occurs. Since the Java virtual machine not only takes time to generate objects, it may also take time to garbage collect and process these objects in the future. Therefore, generating too many objects will have a great impact on the performance of the program.

3. Use local variables as much as possible

The parameters passed when the method is called and the temporary variables created in the call are saved in the stack faster, and other variables, such as static variables, instance variables, etc., are created in the heap at a slower speed. In addition, the variables created in the stack are gone with the end of the method, and no additional garbage collection is required.

4. Close the stream in time

In the process of Java programming, you must be careful when performing database connections and I/O stream operations. After use, close them in time to release resources. Because the operation of these large objects will cause a large system overhead, a little carelessness will lead to serious consequences.

5. Minimize the repeated calculation of variables

Clarify a concept, even if there is only one sentence in the method, it is costly to call a method, including creating a stack frame, protecting the scene when the method is called, and restoring the scene when the method is called. So for example the following operation:

for (int i = 0; i < list.size; i++){…}

It is recommended to replace it with:

for (int i = 0, int length = list.size; i < length; i++){…}

In this way, when the list.size is very large, it reduces a lot of consumption

6. Try to use a lazy loading strategy, that is, create it when needed

E.g:

String str = “aaa”;if (i == 1){list.add(str);}

It is recommended to replace it with:

if (i == 1){String str = “aaa”;list.add(str);}

7. Use exceptions with caution

Anomalies are detrimental to performance. To throw an exception, first create a new object. The constructor of the Throwable interface calls the local synchronization method named fillInStackTrace. The fillInStackTrace method checks the stack and collects call trace information. As long as an exception is thrown, the Java virtual machine must adjust the call stack, because a new object is created during processing. Exceptions can only be used for error handling, and should not be used to control program flow.

8. Don't use try...catch... in the loop, it should be placed on the outermost layer

Except as a last resort. If you write this for no reason, as long as your leader is more senior and obsessive-compulsive, Bacheng will scold you for writing such garbage code.

9. If the length of the content to be added can be estimated, specify the initial length for the collection and tool classes implemented in an array at the bottom layer

Such as ArrayList, LinkedLlist, StringBuilder, StringBuffer, HashMap, HashSet, etc., take StringBuilder as an example:

(1) StringBuilder// By default, a space of 16 characters is allocated

(2) StringBuilder(int size) // The default allocation of space for size characters

(3) StringBuilder(String str) // 16 characters + str.length character space are allocated by default

The initial capacity can be set through the class (not just the StringBuilder above), which can significantly improve performance. Take StringBuilder, for example, length represents the number of characters that the current StringBuilder can hold. Because when the StringBuilder reaches its maximum capacity, it will increase its capacity to 2 times its current capacity and add 2. Whenever StringBuilder reaches its maximum capacity, it has to create a new character array and then add the old characters Copy the contents of the array to the new character array-this is a very performance-consuming operation. Just imagine, if you can estimate that 5000 characters are stored in the character array without specifying the length, the second power of 5000 is 4096, regardless of the increase of 2 for each expansion, then:

(1) On the basis of 4096, apply for a character array of 8194 size, which is equivalent to applying for a character array of 12290 size at one time. If a character array of 5000 size can be specified at the beginning, the savings will be more than doubled. Space;

(2) Copy the original 4096 characters to the new character array.

In this way, both memory space is wasted and the code operation efficiency is reduced. Therefore, it is not wrong to set a reasonable initial capacity for the collection and tool classes implemented by the array at the bottom, which will bring immediate results. Note, however, that a collection like HashMap is implemented as an array + linked list. Don't set the initial size to the same size as you estimated, because the possibility of only connecting one object on a table is almost zero. The initial size is recommended to be set to the Nth power of 2. If it can be estimated that there are 2000 elements, it can be set to new HashMap (128) or new HashMap (256).

10. When copying large amounts of data, use the System.arraycopy command

11. Multiplication and division use shift operations

E.g:

for (val = 0; val < 100000; val += 5){a = val * 8;b = val / 2;}

The shift operation can greatly improve performance, because at the bottom of the computer, the operation of the bit is the most convenient and fastest, so it is recommended to modify it to:
for (val = 0; val <100000; val += 5){a = val << 3;b = val >> 1;}

Although the shift operation is fast, it may make the code not easy to understand, so it is best to add corresponding comments.

12. Don't keep creating object references in the loop

E.g:

for (int i = 1; i <= count; i++){Object obj = new Object;}

This approach will result in count Object references in memory. If the count is large, memory will be consumed. It is recommended to change it to:

Object obj = null;for (int i = 0; i <= count; i++) {obj = new Object; }

In this case, there is only one Object object reference in the memory, and each time a new Object is used, the Object object reference points to a different Object, but there is only one copy in the memory, which greatly saves memory space.

13. Based on the consideration of efficiency and type checking, array should be used as much as possible, and ArrayList should only be used when the size of the array cannot be determined.

14. Try to use HashMap, ArrayList, StringBuilder, unless thread safety is required, it is not recommended to use Hashtable, Vector, StringBuffer, the latter three cause performance overhead due to the use of synchronization mechanisms

15. Don't declare the array as public static final

Because this is meaningless, it just defines the reference as static final, and the contents of the array can be changed at will. Declaring the array as public is a security hole, which means that the array can be changed by external classes.

16. Try to use singletons where appropriate

Using singletons can reduce the burden of loading, shorten loading time, and improve loading efficiency, but not all places are suitable for singletons. In simple terms, singletons are mainly suitable for the following three aspects:

(1) Control the use of resources, and control concurrent access to resources through thread synchronization

(2) Control the generation of instances to achieve the purpose of saving resources

(3) Control the sharing of data, and allow multiple unrelated processes or threads to communicate without establishing a direct association

17. Try to avoid using static variables at will

You know, when an object is referenced by a variable defined as static, gc usually does not reclaim the heap memory occupied by this object, such as:

public class A{ private static B b = new B;}

At this time, the life cycle of static variable b is the same as that of class A. If class A is not unloaded, then the object B pointed to by reference B will stay in memory until the program terminates.

18. Clear the sessions that are no longer needed in time

In order to clear sessions that are no longer active, many application servers have a default session timeout period, which is generally 30 minutes. When the application server needs to save more sessions, if the memory is insufficient, the operating system will transfer part of the data to the disk, and the application server may also dump some inactive sessions to the disk according to the MRU (most frequently used recently) algorithm. It may even throw out of memory exceptions. If the session is to be dumped to disk, it must be serialized first. In a large-scale cluster, the cost of serializing objects is very expensive. Therefore, when the session is no longer needed, the invalidate method of HttpSession should be called to clear the session in time.

19. Collections that implement the RandomAccess interface, such as ArrayList, should use the most common for loop instead of the foreach loop to traverse

This is recommended by the JDK to users. The JDK API's interpretation of the RandomAccess interface is: the implementation of the RandomAccess interface is used to indicate that it supports fast random access. The main purpose of this interface is to allow general algorithms to change its behavior, so that it can provide good results when applied to random or continuous access lists. performance. Practical experience shows that if the class instance that implements the RandomAccess interface is randomly accessed, the efficiency of using a normal for loop will be higher than that of using a foreach loop; conversely, if it is accessed sequentially, using Iterator will be more efficient. You can use code similar to the following to make a judgment:

if (list instanceof RandomAccess)

{ for (int i = 0; i < list.size; i++){}

}else{

Iterator<?> iterator = list.iterable; while (iterator.hasNext){iterator.next}

}

The underlying implementation principle of the foreach loop is the Iterator, see Java Syntactic Sugar 1: Variable Length Parameters and the Principle of Foreach Loop. So the second half sentence "Conversely, if it is accessed sequentially, it will be more efficient to use Iterator" means that the class instances that are accessed sequentially are traversed using a foreach loop.

20. Use synchronous code blocks instead of synchronous methods

This point has been explained very clearly in the article Synchronized lock method block in the multi-threaded module. Unless you can determine that an entire method needs to be synchronized, try to use synchronized code blocks to avoid code that does not need to be synchronized. Synchronization was also carried out, which affected the efficiency of code execution.

21. Declare the constant as static final and name it in uppercase

In this way, these contents can be put into the constant pool during compilation, avoiding the calculation and generation of constant values ​​during runtime. In addition, naming the constants in uppercase can also be used to distinguish constants from variables.

22. Don't create some unused objects, don't import some unused classes

This is meaningless. If "The value of the local variable i is not used" and "The import java.util is never used" appear in the code, then please delete these useless content

23. Avoid using reflection during program operation

About, see reflection. Reflection is a very powerful function that Java provides to users. Powerful functions often mean low efficiency. It is not recommended to use the reflection mechanism especially the frequent use of the reflection mechanism during the running of the program, especially the invoke method of the Method. If it is really necessary, a suggested approach is to use the reflection instance of the classes that need to be loaded through reflection when the project starts. Convert an object and put it into memory—the user only cares about getting the fastest response speed when interacting with the peer, and doesn't care how long it takes for the peer's project to start.

24, use database connection pool and thread pool

These two pools are used to reuse objects, the former can avoid frequent opening and closing of connections, the latter can avoid frequent creation and destruction of threads

25. Use buffered input and output streams for IO operations

Buffered input and output streams, namely BufferedReader, BufferedWriter, BufferedInputStream, BufferedOutputStream, which can greatly improve IO efficiency

26. ArrayList is used in scenarios with more sequential insertion and random access
, and LinkedList is used in scenarios with more element deletion and intermediate insertion . You will know the principles of ArrayList and LinkedList by understanding the principles of ArrayList and LinkedList.

27. Don't let the public method have too many formal parameters

Public methods are methods that are provided to the outside world. If you give these methods too many formal parameters, there are two main disadvantages:

1. It violates the idea of ​​object-oriented programming. Java emphasizes that everything is an object. Too many formal parameters do not fit the idea of ​​object-oriented programming.

2. Too many parameters will inevitably lead to an increase in the error probability of method calls

As for how many "too many" refer to, three or four. For example, we use JDBC to write an insertStudentInfo method. There are 10 student information fields to be inserted into the Student table. These 10 parameters can be encapsulated in an entity class as the formal parameters of the insert method.

28. When string variables and string constants are equals, write string constants in front

This is a relatively common trick, if you have the following code:

String str = “123”;if (str.equals(“123”)) {…}

It is recommended to amend to:

String str = “123”;if (“123”.equals(str)){…}

This is mainly to avoid the null pointer exception

29. Please know that there is no difference between if (i == 1) and if (1 == i) in java, but in terms of reading habits, it is recommended to use the former

Usually someone asks whether there is a difference between "if (i == 1)" and "if (1== i)", which should start with C/C++.

In C/C++, the "if (i == 1)" judgment condition is established. It is based on 0 and non-zero. 0 means false and non-zero means true. If there is such a piece of code:

int i = 2;if (i == 1){…}else{…}

C/C++ judges that "i==1" does not hold, so it is represented by 0, which is false. but if:

int i = 2;if (i = 1) { … }else{ … }

In case the programmer is not careful, write "if (i == 1)" as "if (i = 1)", then there is a problem. Assign i to 1 in the if, if the content in the if judgment is not 0, the return is true, but it is clear that i is 2, and the comparison value is 1, which should return false. This situation is likely to happen in the development of C/C++ and will cause some incomprehensible errors. Therefore, in order to avoid incorrect assignment operations in the if statement by the developer, it is recommended to write the if statement as:

int i = 2;if (1 == i) { … }else{ … }

In this way, even if the developer accidentally writes "1 = i", the C/C++ compiler can check it out the first time, because we can assign i to a variable, but we cannot assign 1 to i to a constant.

However, in Java, the "if (i = 1)" syntax of C/C++ is impossible, because once this syntax is written, Java will compile and report an error "Type mismatch: cannot convert from int to boolean" ". However, although Java's "if (i == 1)" and "if (1 == i)" are not semantically different, it is better to use the former in terms of reading habits.

30. Don't use toString method on arrays

Take a look at what is printed out using toString on the array:

public static void main(String args)

{ int is = new int{1, 2, 3};

System.out.println(is.toString);

}

The result is:

[I@18a992f

The intention is to print out the contents of the array, but it may cause a null pointer exception because the array reference is is null. However, although it does not make sense for the array toString, it is possible to print the contents of the collection to the collection toString, because the parent class AbstractCollections of the collection overrides the toString method of Object.

31. Don't cast down the basic data types that are out of range

This will never get the desired result:

public static void main(String args){ long l = 12345678901234L;int i = (int)l;System.out.println(i);}

We may expect to get some of them, but the result is:

1942892530

explain. Long in Java is 8 bytes and 64 bits, so the representation of 12345678901234 in the computer should be:

0000 0000 0000 0000 0000 1011 0011 1010 0111 0011 1100 1110 0010 1111 1111 0010

An int type data is 4 bytes 32 bits, the first 32 bits of the above string of binary data taken from the low bits are:

0111 0011 1100 1110 0010 1111 1111 0010

This string of binary representation is decimal 1942892530, so it is what we output on the console above. Two conclusions can be drawn from this example by the way:

1. The default data type of integer is int, long l = 12345678901234L, this number has exceeded the range of int, so there is an L at the end, indicating that this is a long type number. By the way, the default type of floating point type is double, so when defining float, it should be written as ""float f = 3.5f"

2. Next, write another sentence "int ii = l + i;" will report an error, because long + int is a long and cannot be assigned to int

32. The data that is not used in the public collection class must be removed in time

If a collection class is public (that is, it is not an attribute in a method), then the elements in the collection will not be automatically released, because there are always references to them. Therefore, if some data in the public collection is not used without removing them, it will cause the public collection to increase continuously, making the system a hidden danger of memory leakage.

33. Convert a basic data type to a string. The basic data type .toString is the fastest way, String.valueOf is the second, and data + "" is the slowest

There are three ways to convert a basic data type to general. I have an Integer type data i. You can use i.toString, String.valueOf(i), i+”” three ways, how efficient the three methods are, see a test :

public static void main(String args)

{

int loopTime = 50000;

Integer i = 0; long startTime = System.currentTimeMillis; for (int j = 0; j < loopTime; j++)

{

String str = String.valueOf(i);

}

System.out.println(“String.valueOf:” + (System.currentTimeMillis - startTime) + “ms”);

startTime = System.currentTimeMillis; for (int j = 0; j < loopTime; j++)

{

String str = i.toString;

}

System.out.println(“Integer.toString:” + (System.currentTimeMillis - startTime) + “ms”);

startTime = System.currentTimeMillis; for (int j = 0; j < loopTime; j++)

{

String str = i + “”;

}

System.out.println(“i + “”:” + (System.currentTimeMillis - startTime) + “ms”);

}

The results of the operation are:

String.valueOf:11ms Integer.toString:5ms i + “”:25ms

Therefore, when converting a basic data type to String in the future, the toString method is preferred. As for why, it's simple:

1. The String.valueOf method calls the Integer.toString method at the bottom, but it will make a null judgment before calling

2. Not to mention the Integer.toString method, just call it directly

3. The bottom layer of i + "" is implemented by StringBuilder, first use the append method to splice, and then use the toString method to get the string

Compared with the three, it is obvious that 2 is the fastest, 1 is the second, and 3 is the slowest.

34. Use the most efficient way to traverse the Map

There are many ways to traverse the Map. Generally, what we need is to traverse the Key and Value in the Map. Then the recommended and most efficient way is:

public static void main(String args)

{

HashMap<String, String> hm = new HashMap<String, String>;

hm.put(“111”, “222”);

Set<Map.Entry<String, String>> entrySet = hm.entrySet;

Iterator<Map.Entry<String, String>> iter = entrySet.iterator; while (iter.hasNext)

{

Map.Entry<String, String> entry = iter.next;

System.out.println(entry.getKey + “\t” + entry.getValue);

}

}

If you just want to traverse the key value of this Map, it is more appropriate to use "Set keySet = hm.keySet;"

35. It is recommended to operate the close of resources separately

Meaning, for example, I have such a piece of code:

try{XXX.close;YYY.close;}catch (Exception e){…}

It is recommended to amend to:

try{ XXX.close; }catch (Exception e) { … }try{ YYY.close; }catch (Exception e) { … }

Although a little troublesome, it can avoid resource leakage. I think if there is no modified code, in case XXX.close throws an exception, then it will enter the cath block, YYY.close will not be executed, and the resource YYY will not be recycled and it will be occupied all the time. A lot of code may cause resource handle leaks. After changing to the above wording, it is guaranteed that XXX and YYY will be closed anyway.

Guess you like

Origin blog.csdn.net/qq_40093255/article/details/113583680