Code optimization summary (quoted from others)

Talk about code optimization:

Code optimization is a very important direction for programmers to study after reaching a certain level. Some people may think it is useless. They think there is something to be modified in some small places, and what will be the big impact on the efficiency of the code if it is modified or not? It is not easy for us to comment on other people's opinions. Take a software that everyone has used as an example. Taobao.com, Taobao.com, especially during Double 11, how many people are online at the same time in the country. Of course, such a program There will be some hardware guarantees and the adoption of some effective software frameworks, but I believe they must have done a lot of work in code optimization to achieve the current results. Made great optimizations. If the project aims to go live without bugs as soon as possible, then you can focus on the big and let go of the small, and the details of the code can be polished; but if there is enough time to develop and maintain the code, you must consider every detail that can be optimized at this time. , the accumulation of small optimization points will definitely improve the running efficiency of the code.
The goal of code optimization:
from the perspective of space, compression space: that is, the storage space of compressed code, I think the most important point is code reuse;
from the perspective of time, compression time: that is, the running time of the compressed code, improve the running time of the code effectiveness.

Code optimization details:

1. Try to specify the final modifier of classes and methods.
A class with the final modifier is not derivable. In the Java core API, there are many examples of the application of final, such as java.lang.String, the entire class is final. Specifying the final modifier for a class prevents the class from being inherited, and specifying the final modifier for a method prevents the method from being overridden. If a class is designated as final, all methods of that class are final. The Java compiler will look for opportunities to inline all final methods. Inlining plays an important role in improving the efficiency of Java. For details, see Java runtime optimization. This approach can improve performance by an average of 50%.

2. Reuse objects as much as possible.
Especially for the use of String objects, StringBuilder/StringBuffer should be used instead when string concatenation occurs. Since the JVM not only spends time generating objects, but may also spend time garbage collecting and processing these objects in the future, generating too many objects will have a great impact on the performance of the program. The main performance difference between String type and StringBuffer type is that String is an immutable object, so every time you change the String type, it is actually equivalent to generating a new String object, and then pointing the pointer to the new String object, Therefore, it is best not to use String for strings that often change the content, because every time an object is generated, it will affect the system performance, especially when there are many unreferenced objects in the memory, the JVM's GC will start to work, and the speed will definitely be slower. of. If you use the StringBuffer class, the result will be different. Each time the result of the operation will operate on the StringBuffer object itself, instead of generating a new object like the String type, and then changing the object reference. So in general it is recommended to use StringBuffer, especially when the string object changes frequently. However, in some special cases, the string splicing of String objects is actually interpreted by the JVM as the splicing of StringBuffer objects, so the speed of using String objects at these times is not slower than StringBuffer objects, especially in the following string objects In the generation process, the efficiency of using String type is much faster than StringBuffer type:
String s = "This is only a" + " simple" + " test";
StringBuffer sb = new StringBuilder("This is only a").append( "simple").append("test");
The former is much faster than the object generated by the latter. At this time, StringBuffer has no advantage at all in terms of speed. This is a processing mechanism of the JVM. When the JVM sees String s = "This is only a" + " simple" + " test";, it will treat it as String s = "This is only a simple test";, so of course It doesn't take much time. But it should be noted here that if your string is from another String object, the speed is not so fast, such as:
String s2 = "This is only a";
String s3 = " simple";
String s4 = " test";
String s1 = s2 +s3 + s4;
At this time, the JVM will deal with it in the original way.
java.lang.StringBuilder is a variable character sequence, which was added in JDK1.5. This class provides an API compatible with StringBuffer, but synchronization is not guaranteed. This class is designed to be used as a drop-in replacement for StringBuffer when the string buffer is used by a single thread (which is common). If it is not a multi-threaded environment, it is recommended to use StringBuilder first, because StringBuilder is faster than StringBuffer in most implementations. Both methods are basically the same.

3. Use local variables as much as possible.
The parameters passed when calling the method and the temporary variables created during the call are stored in the stack faster, and other variables, such as static variables, instance variables, etc., are created in the heap, which is slower. In addition, the variables created in the stack are gone as the method ends, 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 connection and I/O stream operations. After use, close it in time to release resources. Because the operation of these large objects will cause a lot of resource overhead to the system, a little carelessness will lead to serious consequences.
5. Minimize repeated calculation of variables.
Clarify a concept, calling a method, even if there is only one statement in the method, is consuming, including creating a stack frame, protecting the scene when calling a method, and restoring the scene when the method is called. So for example the following operation:
for (int i = 0; i < list.size(); i++)
{...}
is recommended to be replaced by:
for (int i = 0, int length = list.size(); i < length; i++)
{...}
In this way, when list.size() is large, it reduces a lot of consumption.
6. Try to use the lazy loading strategy, that is, start creating when you need it, and don't create it when you don't need it.
For example:
String str = "aaa";
if(i == 1)
{
  list.add(str);
}
is recommended to be replaced with:
if(i == 1)
{
  String str = "aaa";
  list.add(str);
}
7. Use exceptions with caution.
Exceptions are detrimental to performance. Throwing an exception first creates a new object. The constructor of the Throwable interface calls a local synchronization method named fillInStackTrace(). The fillInStackTrace() method checks the stack and collects call trace information. Whenever an exception is thrown, the JVM has to adjust the call stack because a new object is created during processing. Exceptions should 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 in the outermost layer, unless forced.
9. If the length of the content to be added can be estimated in advance, specify the initial length for the underlying collection and tool class implemented in the form of an array.
For example, ArrayList, LinkedLlist, StringBuilder, StringBuffer, HashMap, HashSet, etc. Take StringBuilder as an example:
(1) StringBuilder() // 16 characters are allocated by default
(2) StringBuilder(int size) // size characters are allocated by default Space
(3) StringBuilder(String str) // Default allocation of 16 characters + str.length() character space
You can set its initialization capacity through the class (here refers not only to the StringBuilder above), which can significantly improve performance. For example StringBuilder, length indicates the number of characters that the current StringBuilder can hold. Because when StringBuilder reaches its maximum capacity, it will increase its own capacity to 2 times the current capacity plus 2, whenever StringBuilder reaches its maximum capacity, it has to create a new character array and then replace the old character Copying the contents of the array into a new character array is a very expensive operation. Just imagine, if it can be estimated that about 5000 characters will be stored in the character array without specifying the length, the nearest power of 2 to 5000 is 4096, and 2 is added for each expansion, then:
(1) On the basis of 4096, Applying for a character array of 8194 sizes is equivalent to applying for a character array of 12290 sizes at a time. If you can specify a character array of size 5000 at the beginning, it will save more than double the space;
(2) Put the original 4096 characters are copied into the new character array.
In this way, the memory space is wasted and the code running efficiency is reduced. Therefore, it is wise to set a reasonable initialization capacity for the underlying collections and utility classes implemented by arrays, which will bring immediate results. However, note that for a collection implemented as an array + linked list like HashMap, do not set the initial size to be the same as your estimated size, because the possibility of only connecting one object to a table is almost 0. The initial size is recommended to be set to the 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 a large amount of data, use the System.arraycopy() command.
11. Multiplication and division use shift operations.
For example:
for(val = 0; val < 100000; val += 5)
{
  a = val * 8;
  b = val / 2;
}
Using the displacement operation can greatly improve the 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 as:
for(val = 0; val < 100000; val + = 5)
{
  a = val << 3;
  b = val >> 1;
}
Although the displacement operation is fast, it may reduce the readability of the code. Therefore, in order to increase developers and facilitate maintenance later, it is best to add the corresponding Notes.
12. Do not keep creating object references in the loop.
For example:
for(int i = 1; i <= count; i++)
{
  Object obj = new Object();
}
This approach will result in the existence of count Object object references in memory. If the count is large, it will be extremely expensive Heap memory, it is recommended to modify it to:
Object obj = null; for
(int i = 0; i <= count; i++)
{
  obj = new Object()
;
When new Object() is used, the Object object reference points to different Objects, but there is only one copy in the memory, which greatly saves memory space. It is recommended that when thinking about this part, compare it with lazy loading, which may be better understood.
13. Based on the consideration of efficiency and type checking, Array should be used as much as possible, and ArrayList should be used only when the size of the array cannot be determined.
14. Try to use HashMap, ArrayList, and StringBuilder. Unless thread safety is required, it is not recommended to use Hashtable, Vector, and StringBuffer. The latter three will cause performance overhead due to the use of synchronization mechanisms.
15. Do not declare arrays as public static final.
Because this is meaningless, it just defines the reference as static final, the contents of the array can still be changed at will, and declaring the array as public is a security hole, which means that the array can be changed by external classes.
16. Try to use the singleton pattern when appropriate.
Using a singleton can reduce the burden of loading, shorten the loading time, and improve the efficiency of loading, but not all places are suitable for singletons. In short, singletons are mainly suitable for the following three aspects:
(1) Controlling resources Use, control the concurrent access of 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 Communication between processes or threads.
17. Try to avoid the random use of static variables.
You must know that when an object is referenced by a variable defined as static, then gc usually does not reclaim the heap memory occupied by the object, such as:
public class A
{
  private static B b = new B();
}
this The life cycle of the static variable b is the same as that of class A. If class A is not unloaded, the b object pointed to by reference B will reside in memory until the program terminates.
18. Clear sessions that are no longer needed in a timely manner.
To purge sessions that are no longer active, many application servers have a default session timeout, typically 30 minutes. When the application server needs to save more sessions, if the memory is insufficient, the operating system will transfer some data to the disk, and the application server may also dump some inactive sessions to the disk according to the MRU (most recently used) algorithm. It might even throw an out of memory exception. If the session is to be dumped to disk, it must be serialized first, and in large-scale clusters, serializing objects is expensive. Therefore, when the session is no longer needed, the invalidate() method of HttpSession should be called in time to clear the session.
19. Collections that implement the RandomAccess interface, such as ArrayList, should be traversed using the most common for loop instead of a foreach loop.
This is what the JDK recommends 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 a general algorithm to change its behavior, so that it can provide good performance when applied to random or continuous access lists. performance. Practical experience shows that if the class instance implementing the RandomAccess interface is randomly accessed, the efficiency of using a normal for loop will be higher than that of using a foreach loop; on the contrary, if it is accessed sequentially, it will be more efficient to use an Iterator. You can use code like the following for 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 Iterator, see Java Syntax Sugar 1: Variable Length Parameters and Foreach Loop Principle. So the second half of the sentence "Conversely, if it is accessed sequentially, it will be more efficient to use Iterator" means that those class instances accessed sequentially, use the foreach loop to traverse.
20. Use synchronized code blocks instead of synchronized methods.
This has been made clear 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. It is also synchronized, which affects the efficiency of code execution.
21. Declare constants as static final and name them in uppercase.
This puts these contents into the constant pool at compile time and avoids calculating the value of the generated constant at runtime. In addition, naming constants in uppercase can also facilitate the distinction between constants and variables.
22. Don't create some unused objects, and don't import some unused classes.
This is meaningless, if there are "The value of the local variable i is not used", "The import java.util is never used" in the code, then please delete these useless content.
23. Avoid using reflection during program operation.
Reflection is a very powerful function provided by Java to users. Powerful functions often mean inefficiency. It is not recommended to use the reflection mechanism, especially the invoke method of Method, during the running process of the program. If it is really necessary, a suggested approach is to use the reflection instance for those classes that need to be loaded through reflection when the project starts. When an object is created and put into memory, the user only cares about getting the fastest response speed when interacting with the peer, and does not care how long it takes for the peer project to start.
24. Use database connection pool and thread pool.
Both of these pools are used to reuse objects, the former avoids opening and closing connections frequently, and the latter avoids creating and destroying threads frequently.
25. Use buffered input and output streams for IO operations.
Buffered input and output streams, namely BufferedReader, BufferedWriter, BufferedInputStream, and BufferedOutputStream, can greatly improve IO efficiency.
26. ArrayList is recommended for scenarios where elements require sequential insertion and random access, and LinkedList is recommended for scenarios where elements need to be deleted and inserted in between. This, understand the principle of ArrayList and LinkedList.
27. Don't let public methods have too many parameters.
Public methods are methods provided to the outside world. If there are too many formal parameters for these methods, 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, and object-oriented programming. (2) Too
many parameters will inevitably lead to an increase in the error probability of method calls.
As for the number of "too many", let's say 3 or 4. 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 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 modify it to:
String str = "123";
if("123".equals(str))
{
...
}
This is mainly to avoid null pointer exceptions.
29. There is no difference between if (i == 1) and if (1 == i) in Java, but from reading habits, it is recommended to use the former.
Is there any difference between "if(i == 1)" and "if(1== i)"?
In C/C++, the "if(i == 1)" judgment condition is established, based on 0 and non-0, 0 means false, non-0 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 accidentally writes "if(i == 1)" as "if(i = 1)", there will be a problem. Assign i to 1 within the if, if judges that the content inside is not 0, the returned value is true, but obviously i is 2, the comparison value is 1, and it should return false. This situation is very likely to occur in the development of C/C++ and will lead to some incomprehensible errors. Therefore, in order to avoid incorrect assignment operations in the if statement, 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 immediately, because We can assign i to 1 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 the error "Type mismatch: cannot convert from int to boolean" ". However, although there is no semantic difference between Java's "if(i == 1)" and "if(1 == i)", from a reading habit, it is better to use the former.
30. Do not use the toString() method on arrays.
Take a look at what is printed using toString() on an array:
public static void main(String[] args)
{
  int[] is = new int[]{1, 2, 3};
  System.out.println(is.toString());
}
The result is:
[I@18a992f
is intended to print out the contents of the array, but it may cause a null pointer exception because the array reference is null. However, although toString() is meaningless for arrays, toString() for collections can print out the contents of the collection, because the parent class of collections, AbstractCollections, overrides the toString() method of Object.
31. Don't downcast basic data types that are out of range.
This will never give the desired result:
public static void main(String[] args)
{
  long l = 12345678901234L;
  int i = (int)l;
  System.out.println(i);
}
We might expect one of A few, but the result is:
1942892530
Explain. In Java, long 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 data is 4 bytes of 32 bits , the first 32 bits of the above string of binary data are taken from the low order:
0111 0011 1100 1110 0010 1111 1111 0010
This string in binary is 1942892530 in decimal, so that's what we output on the console above. From this example, two conclusions can be drawn 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 float is double, so when defining float, write "float f = 3.5f".
(2) Next, writing a sentence "int ii = l + i;" will report an error, because long + int is a long and cannot be assigned to int.
32. The data not used in the public collection class must be removed in time.
If a collection class is public (that is, not a property in a method), then the elements in the collection are not automatically released because there are always references to them. Therefore, if some data in the public collection is not used and is not removed, it will cause the public collection to continue to grow, making the system vulnerable to memory leaks.
33. Convert a basic data type to a string. Basic data type.toString() is the fastest way, String.valueOf(data) is the second, and data+"" is the slowest way.
There are generally three ways to convert a basic data type into a string. I have an Integer data i, and I can use three methods: i.toString(), String.valueOf(i), and i+"". How efficient are the three methods? , 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 result of the operation is:
}

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 very simple:
(1) The String.valueOf() method calls the Integer.toString() method at the bottom, but it will make a short judgment before calling it.
(2) The Integer.toString() method is not mentioned, and is called directly.
(3) The bottom layer of i + "" is implemented by StringBuilder, which is spliced ​​with the append method first, and then the toString() method is used to obtain the string.
Comparing 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 Map. Usually, we need to traverse the Key and Value in Map. 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();


    Map.Entry<String, String> entry = iter.next();
    System.out.println(entry.getKey() + "\t" + entry.getValue());
  }
}
If you just want to traverse this Map the key value, it would be more appropriate to use "Set keySet = hm.keySet();".
35. Separate operations are recommended for the close() operation of resources.
For example:
try
{
  XXX.close();
  YYY.close();
}
catch (Exception e)
{ ... }
is recommended to be modified to:
try
{
  XXX.close();
}
catch (Exception e)
{ ... }
try
{
  YYY.close();
}
catch (Exception e)
{ ... }
Although it is a bit troublesome, it can avoid resource leakage. The hidden danger of the first piece of code: if there is no modified code, in case XXX.close() throws an exception, it will enter the catch block, YYY.close() will not be executed, and the YYY resource will not be executed. It has been recycled and occupied all the time. If there is too much code like this, it may cause resource handle leakage. After changing to the above writing, it is guaranteed that XXX and YYY will be closed no matter what the circumstances.

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=326262665&siteId=291194637