Optimize programs through Java code specifications, optimize memory usage, and prevent memory leakage
The resources available for programs to utilize (memory, CPU time, network bandwidth, etc.) are limited. The purpose of optimization is to enable programs to complete scheduled tasks with as few resources as possible. Optimization usually includes two aspects: reducing the size of the code and improving the running efficiency of the code. This article mainly discusses how to improve the efficiency of the code.
In Java programs, most of the reasons for performance problems are not in the Java language, but in the program itself. It is very important to develop good code writing habits, such as correctly and cleverly applying the java.lang.String class and java.util.Vector class, which can significantly improve the performance of the program. Let’s analyze this issue in detail below.
1. Try to specify that the final modifier of the class. Classes with final modifiers with final modifiers are not derived.
In the Java core API, there are many examples of applying final, such as java.lang.String. Specifying final for String class prevents people from overwriting the length() method. In addition, 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 (this is related to the specific compiler implementation). This move can improve performance by an average of 50%.
2. Try to reuse the object.
Especially when using String objects, StringBuffer is used instead when string concatenation occurs. Since the system 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. Try to use local variables. The parameters passed when calling the method and the temporary variables created in the call are stored in the stack (Stack), which is faster.
Other variables, such as static variables, instance variables, etc., are created in the heap and are slower. In addition, local variables may be further optimized depending on the specific compiler/JVM. See "Use stack variables whenever possible".
4. Do not repeat initialization of variables <br />By default, when calling the class constructor, Java will initialize the variable to a certain value: all objects are set to null, integer variables (byte, short, int, long) Set to 0, float and double variables are set to 0.0, and logical values are set to false. This should be especially noticed when a class is derived from another, because when an object is created with the new keyword, all constructors in the constructor chain are automatically called.
5. In the development of JAVA + ORACLE application system, try to use uppercase form in Java to reduce the parsing burden of the ORACLE parser.
6. During Java programming, be careful when performing database connections and I/O streaming operations. After use, close them to release resources.
Because the operation of these large objects will cause large system overhead, and if you are not careful, it will lead to serious consequences.
7. Since the JVM has its own GC mechanism, it does not require too much consideration from program developers, which reduces the burden on developers to a certain extent, but also misses hidden dangers. Excessive creation of objects will consume a large amount of memory in the system. In severe cases, memory leakage will be caused, so it is of great significance to ensure the timely recycling of expired objects .
The condition for JVM to recycle garbage is that the object is not referenced; however, the JVM's GC is not very witty, and even if the object meets the conditions for garbage collection, it will not be recycled immediately. Therefore, it is recommended that we manually set it to null after the object is used.
8. When using the synchronization mechanism, try to use method synchronization instead of code block synchronization.
9. Minimize repeated calculations of variables <br />For example: for(int i = 0;i < list.size; i ++) {
…
}
Should be replaced by:
for(int i = 0,int len = list.size();i < len; i++){
…
}
10. Try to adopt the lazy loading strategy, that is, start creating when needed.
For example: String str = “aaa”;
if(i == 1) {
list.add(str);
}
Should be replaced by:
if(i == 1) {
String str = “aaa”;
list.add(str);
}
11. Use abnormalities with caution
Abnormality is not good for performance. To throw an exception, you must first create a new object. The constructor of the Throwable interface calls the local (Native) method named fillInStackTrace(), and the fillInStackTrace() method checks the stack and collects call trace information. As long as an exception is thrown, the VM 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.
12. Do not use it in a loop:
Try {
} catch() {
}
It should be placed on the outermost layer.
13. Use of StringBuffer:
StringBuffer represents a variable, writable string.
There are three construction methods:
StringBuffer (); //Default allocated 16 characters of space
StringBuffer (int size); //Assign space to size characters
StringBuffer (String str); //Assign 16 characters + str.length() character space You can set its initialization capacity through the StringBuffer constructor, which can significantly improve performance.
The constructor mentioned here is StringBuffer(int length), and the length parameter indicates the number of characters that the current StringBuffer can hold. You can also use the ensureCapacity(int minimum capacity) method to set its capacity after the StringBuffer object is created. First, let’s take a look at the default behavior of StringBuffer, and then find a better way to improve performance.
StringBuffer maintains a character array internally. When you use the default constructor to create a StringBuffer object, because the initialization character length is not set, the capacity of StringBuffer is initialized to 16 characters, which means the default capacity is 16 characters. . When the StringBuffer reaches its maximum capacity, it will increase its capacity to 2 times the current capacity and add 2, that is (2*old value +2). If you use the default value, after initialization, then add characters to it. When you append to the 16th character, it will increase the capacity to 34 (2*16+2), and when append to 34 characters, it will Increase capacity to 70 (2*34+2). No matter what, as long as the StringBuffer reaches its maximum capacity, it has to create a new character array and then re-copy both the old and new characters. This is a bit too expensive. Therefore, it is not wrong to always set a reasonable initialization capacity value for StringBuffer, which will bring immediate performance gain. This shows the role of adjusting the StringBuffer initialization process. So, using a suitable capacity value to initialize a StringBuffer is always an optimal suggestion.
14. Use the Java class java.util.Vector reasonably.
Simply put, a Vector is an array of java.lang.Object instances. Vector is similar to an array, and its elements can be accessed through an index in the form of an integer. However, after the creation of an object of Vector type, the size of the object can be expanded and reduced according to the addition or deletion of elements. Please consider the following example of adding elements to Vector:
Object bj = new Object();
Vector v = new Vector(100000);
for(int I=0;
I<100000; I++) { v.add(0,obj); }
Unless there is absolutely sufficient reason to require new elements to be inserted in front of Vector every time, the above code is bad for performance. In the default constructor, the initial storage capacity of Vector is 10 elements. If the storage capacity is insufficient when new elements are added, the storage capacity will be doubled every time in the future. The Vector class is like the object StringBuffer class. Every time the storage capacity is expanded, all existing elements must be copied to the new storage space. The following code snippet is orders of magnitude faster than the previous example:
Object bj = new Object();
Vector v = new Vector(100000);
for(int I=0; I<100000; I++) { v.add(obj); }
The same rule applies to the remove() method of the Vector class. Since each element in Vector cannot contain a "space" between each element, deleting any other element except the last element causes the elements after the deleted element to move forward. That is, deleting the last element from a Vector is several times less "overhead" than deleting the first element.
Assuming that we want to remove all elements from the previous Vector, we can use this code:
for(int I=0; I<100000; I++)
{
v.remove(0);
}
However, compared to the following code, the previous code is orders of magnitude slower:
for(int I=0; I<100000; I++)
{
v.remove(v.size()-1);
}
The best way to delete all elements from an object v of type Vector is:
v.removeAllElements();
Suppose that object v of type Vector contains the string "Hello". Consider the following code, which removes the "Hello" string from this Vector:
String s = "Hello";
int i = v.indexOf(s);
if(I != -1) v.remove(s);
The code looks like nothing wrong, but it is also bad for performance. In this code, the indexOf() method searches v in sequence to find the string "Hello", and the remove(s) method also needs to search in the same order. The improved version is:
String s = "Hello";
int i = v.indexOf(s);
if(I != -1) v.remove(i);
In this version, we directly give the exact index position of the element to be deleted in the remove() method, thereby avoiding the second search. A better version is:
String s = "Hello"; v.remove(s);
Finally, let's look at a code snippet about the Vector class:
for(int I=0; I++;I < v.length)
If v contains 100,000 elements, this code snippet will call the v.size() method 100,000 times. Although the size method is a simple method, it still requires the overhead of a method call, at least the JVM needs to configure and clear the stack environment for it. Here, the code inside the for loop will not modify the size of the Vector type object v in any way, so the above code is best rewritten into the following form:
int size = v.size(); for(int I=0; I++;I<size)
While this is a simple change, it still wins performance. After all, every CPU cycle is precious.
15. When copying a large amount of data, use the System.arraycopy() command.
16. Code refactoring: Enhance the readability of the code .
For example:
public class ShopCart {private List carts ;…public void add (Object item) {if(carts == null) {carts = new ArrayList();}crts.add(item);}public void remove(Object item) {if (carts. contains(item)) {carts.remove(item);}}public List getCarts() {//Return read-only list return Collections.unmodifiableList(carts);}//This method is not recommended//this. getCarts().add(item);} 17. Create an instance of a class without using new keywords
When creating an instance of a class with the new keyword, all constructors in the constructor chain will be called automatically. But if an object implements the Cloneable interface, we can call its clone() method. The clone() method does not call any class constructors.
When using Design Pattern, if you use Factory mode to create an object, it is very simple to use the clone() method to create a new object instance. For example, the following is a typical implementation of the Factory pattern:
public static Credit getNewCredit() {
return new Credit();
}
The improved code uses the clone() method as follows:
private static Credit BaseCredit = new Credit();
public static Credit getNewCredit() {
return (Credit) BaseCredit.clone();
}
The above idea is also very useful for array processing.
18. Multiplication and division
Consider the following code:
for (val = 0; val < 100000; val +=5) {
alterX = val * 8; myResult = val * 2;
}
Replacing multiplication with shift operations can greatly improve performance. Here is the modified code:
for (val = 0; val < 100000; val += 5) {
alterX = val << 3; myResult = val << 1;
}
The modified code no longer multiplies by 8, but instead uses the equivalent left shift of 3 bits, with 1 bit per left shift equivalent to multiplying by 2. Accordingly, the right shift by 1 bit operation is equivalent to dividing by 2. It is worth mentioning that although the shift operation is fast, it may make the code difficult to understand, so it is best to add some comments.
19. Close useless sessions in JSP page.
A common misunderstanding is that the session is created when there is client access. However, the fact is that it is not created until a server program calls a statement such as HttpServletRequest.getSession(true). Note that if the JSP does not display it, use <> to close it. session, then when the JSP file is compiled into Servlet, this statement will be automatically added to HttpSession session = HttpServletRequest.getSession(true); This is also the origin of the session object implicit in JSP. Since session consumes memory resources, if you don't plan to use session, you should close it in all JSPs.
For pages that do not need to track session status, closing automatically created sessions can save some resources. Use the following page directive: <%@ page session="false"%>
20. JDBC and I/O
If an application needs to access a large-scale dataset, you should consider using block extraction. By default, JDBC extracts 32 rows of data each time. For example, suppose we want to traverse a record set of 5000 rows, JDBC must call the database 157 times before it can extract all the data. If the block size is changed to 512, the number of calls to the database will be reduced to 10 times.
21. Servlet and memory usage <br />Many developers save a large amount of information to user sessions at will. Sometimes, objects stored in the session are not recycled by the garbage collection mechanism in time. From a performance perspective, a typical symptom is that the user feels that the system is slowing down periodically, but cannot attribute the reason to any specific component. If you monitor the JVM's heap space, it is manifested as abnormal memory usage fluctuations.
There are two main ways to solve this type of memory problem. The first method is to implement the HttpSessionBindingListener interface in all beans with a scope of session. In this way, as long as the valueUnbound() method is implemented, the resources used by the bean can be explicitly released.
Another way is to invalidate the session as soon as possible. Most application servers have the option to set the session invalidation interval. In addition, the session's setMaxInactiveInterval() method can also be called programmatically. This method is used to set the maximum interval time allowed by the Servlet container to the client request in seconds before the invalidation session.
22. Use buffer marks
Some application servers have added buffer marking function for JSP. For example, BEA's WebLogic Server supports this feature since version 6.0, and the Open Symphony project also supports this feature. JSP buffering tags can buffer both page fragments and the entire page. When the JSP page is executed, if the target fragment is already in the buffer, the code that generates the fragment will no longer need to be executed. Page-level buffering captures requests to the specified URL and buffers the entire result page. This feature is extremely useful for shopping baskets, catalogs, and portal homepages. For such applications, page-level buffering can save the results of page execution for subsequent requests.
23. Choose the right citation mechanism
In a typical JSP application system, the header and footer parts are often extracted, and then the header and footer are introduced as needed. Currently, there are two main methods to introduce external resources into JSP pages: include directives and include actions.
include directive: For example <%@ include file="copyright.html" %>. This directive introduces the specified resource at compile time. Before compilation, the page with the include directive and the specified resource are merged into a file. The referenced external resources are determined at compile time, which is more efficient than determining the resources at runtime.
include action: For example <jsp:include page="copyright.jsp" />. This action introduces the result generated after the specified page is executed. Since it completes at runtime, control of output results is more flexible. However, it is only cost-effective to use include action when the quoted content is frequently changed, or when the referenced page cannot be determined before the request for the main page appears.
24. Clear no longer needed sessions in time
In order to clear sessions that are no longer active, many application servers have a default session timeout, usually 30 minutes. When the application server needs to save more sessions, if the memory capacity is insufficient, the operating system will transfer part of the memory data to disk. The application server may also transfer part of the inactive sessions based on the "Most Recently Used" algorithm Store to disk and may even throw an "out of memory" exception. In large-scale systems, serialization sessions are expensive. When the session is no longer needed, the HttpSession.invalidate() method should be called in time to clear the session. The HttpSession.invalidate() method can usually be called on the application's exit page.
25. Do not declare array as: public static final.
26. Discussion on the traversal efficiency of HashMap
There are often traversal operations on key and value pairs in HashMap, and there are two methods: Map<String, String[]> paraMap = new
HashMap<String, String[]>();............//The first loop Set<String> appFieldDefIds = paraMap.keySet(); for (String appFieldDefId: appFieldDefIds) {String[] values = paraMap.get(appFieldDefId);......}//The second loop for(Entry<String, String[]> entry : paraMap.entrySet()){String appFieldDefId = entry.getKey();String[] values = entry.getValue();.......} The first implementation is significantly less efficient than the second implementation.
Analysis is as follows Set<String> appFieldDefIds = paraMap.keySet(); First get keySet from HashMap
The code is as follows:
public Set<K> keySet() {Set<K> ks = keySet;return (ks != null ? ks : (keySet = new KeySet()));}private class KeySet extends AbstractSet<K> {public Iterator<K > iterator() {return newKeyIterator();}public int size() {return size;}public boolean contains(Object o) {return containsKey(o);}public boolean remove(Object o) {return HashMap.this.removeEntryForKey (o) != null;}public void clear() {HashMap.this.clear();}}In fact, it returns a private class KeySet, which is inherited from AbstractSet and implements the Set interface.
Let's take a look at the syntax of for/in loops
for(declaration: expression)
statement
During the execution phase, it is translated into the following formulas
for(Iterator<E> #i = (expression).iterator(); #i.hashNext();){
declaration = #i.next();
statement
}
Therefore, HashMap.keySet().iterator() is called in the first for statement for (String appFieldDefId: appFieldDefIds)
This method calls newKeyIterator()
Iterator<K> newKeyIterator() {
return new KeyIterator();
}
private class KeyIterator extends HashIterator<K> {
public K next() {
return nextEntry().getKey();
}
}
So in the for, the Iterator used in the second loop for (Entry<String, String[]> entry: paraMap.entrySet()) is called as follows.
kind
private class EntryIterator extends HashIterator<Map.Entry<K,V>> {
public Map.Entry<K,V> next() {
return nextEntry();
}
}
At this time, the first loop gets the key, and the second loop gets the Entry efficiency of HashMap is that the second loop reflected in the loop. Therefore, you can directly take the key and value values, and the first loop still needs to use HashMap's get( Object key) to get value value Now look at HashMap's get(Object key) method
public V get(Object key) {
Object k = maskNull(key);
int hash = hash(k);
int i = indexFor(hash, table.length); //Entry[] table
Entry<K,V> e = table;
while (true) {
if (e == null)
return null;
if (e.hash == hash && eq(k, e.key))
return e.value;
e = e.next;
}
}
In fact, it is to use the Hash value to get the corresponding Entry again to compare and get the result. Therefore, using the first loop is equivalent to entering the HashMap twice.
In the second loop obtains the Entry value and then directly take the key and value, which is more efficient than the first loop. In fact, according to the concept of Map, it should be better to use the second loop. It is originally a value pair of key and value. It is not a good choice to operate the key and value separately.
27. Use of array (array) and ArryList
array([]): the most efficient; but its capacity is fixed and cannot be changed dynamically;
ArrayList: Capacity can grow dynamically; but sacrifice efficiency;
Based on efficiency and type verification, array should be used as much as possible. ArrayList should be used only if the array size cannot be determined!
ArrayList is a complex version of Array
ArrayList encapsulates an Object-type array. In general, it has no essential difference from an array. Even many methods of ArrayList, such as Index, IndexOf, Contains, Sort, etc., are directly based on the internal array. Call the corresponding method of Array.
When ArrayList stores an object, the type information is discarded and all objects are blocked as Object. The type is not checked during compilation, but an error will be reported at runtime.
Note: jdk5 has added support for generics, and type checking can be performed when using ArrayList.
From this point of view, the difference between ArrayList and array is mainly due to the efficiency of dynamic capacity increase.
28. Try to use HashMap and ArrayList . Unless necessary, HashTable and Vector are not recommended. The latter has performance overhead due to the use of synchronization mechanism.
29. The difference between StringBuffer and StringBuilder:
java.lang.StringBuffer thread-safe mutable character sequence. A String-like string buffer, but cannot be modified.
StringBuilder. Compared to this class, the java.lang.StringBuilder class should usually be preferred because it supports all the same operations, but is faster because it does not perform synchronization. For better performance, the capacity of StirngBuffer or StirngBuilder should be specified as much as possible. Of course, if the string you operate does not exceed 16 characters in length, you won't need it. In the same case, using StirngBuilder can only achieve about 10%-15% performance improvement compared to using StringBuffer, but it takes the risk of multi-threading insecure. In real modular programming, the programmer responsible for a certain module may not be able to clearly determine whether the module will be put into a multi-threaded environment, so: unless you can determine that the bottleneck of your system is on the StringBuffer , and make sure that your module will not run in multithreaded mode, otherwise use StringBuffer.
Other supplements:
1. Clear objects that are no longer used in time and set to null
2. Use final, static and other keywords as much as possible
3. Use buffered objects as much as possible
How to optimize code to make JAVA source file and compiled CLASS file smaller
1 Try to use inheritance. The more methods you inherit, the less code you want to write.
2 Open the optimization options of the JAVA compiler: javac -O This option will delete the line number in the CLASS file and declare some private, static, and final small-segment methods as inline method calls
3 Extract the common code
4 Don't initialize large arrays. Although initializing an array is just a quantity of code in JAVA code, the compiled code is to insert elements of an array by line of code, so if you have a large amount of data that needs to be stored in an array, you can First put this data in a String, and then parse the string into an array during runtime
5. Date type objects will take up a lot of space. If you want to store a large number of date objects, you can consider storing them as
long type, then convert to Date type when used
6. Try to use short names for class names, method names and variable names. You can consider using Hashjava, Jobe, Obfuscate and Jshrink and other tools to automatically complete this work.
7 Define variables of static final type into the Interface
8 If arithmetic operations can be used for left/right movement, do not use * and / operations. Do not use the same operations multiple times.
2. Do not initialize variables twice
Java initializes the variable to a known value by default by calling a unique class constructor. All objects are set to null, integers (byte, short, int, long) are set to 0, float and double are set to 0.0, and Boolean variables are set to false. This is especially important for classes that extend from other classes, just like all the series of constructors are automatically called when creating an object using a new keyword.
3. Make the class Final wherever possible
Classes marked final cannot be extended. There are a lot of examples of this technology in the Core Java API, such as java.lang.String. Marking the String class as final prevents developers from creating length methods they implement themselves.
To put it more deeply, if the class is final, all the methods of the class are final. The Java compiler may inline all methods (this depends on the compiler's implementation). In my tests, I've seen an average increase in performance by 50%.
9. The exception is thrown where it needs to be thrown. If the try catch can be integrated, it will be integrated.
try { some.method1(); // Difficult for javac } catch( method1Exception e ) { // and the JVM runtime // Handle exception 1 // to optimize this } // code try { some.method2(); } catch ( method2Exception e ) { // Handle exception 2 }try { some.method3(); } catch( method3Exception e ) { // Handle exception 3 } The code that has been downloaded is easier to be optimized by the compiler
try { some.method1(); // Easier to optimize some.method2(); some.method3(); } catch( method1Exception e ) { // Handle exception 1 } catch( method2Exception e ) { // Handle exception 2 } catch( method3Exception e ) { // Handle exception 3 }
10. Optimization of For loop
Replace…
for( int i = 0; i < collection.size(); i++ ) {
...
}
with…
for( int i = 0, n = collection.size(); i < n; i++ ) {
...
}
5. In the development of JAVA + ORACLE application system, try to use uppercase form embedded SQL statements in Java to reduce the parsing burden of the ORACLE parser.
10. Try to adopt the lazy loading strategy, that is, start creating when needed.
For example: String str = “aaa”;
if(i == 1) {
list.add(str);
}
Should be replaced by:
if(i == 1) {
String str = “aaa”;
list.add(str);
}
12. Do not use it in a loop:
Try {
} catch() {
}
It should be placed on the outermost layer
The above is all about this article. I hope it will be helpful to everyone's Java optimization.
Please take some time to share the article with your friends or leave a comment. We will sincerely thank you for your support!