22 error optimization strategy

Keywords: Programming Java Spring JDK

This blog is from my new book Java System Performance Optimization (tentative name), and welcome to read my new book. The Essence of Spring Boot 2

4.22.1 final cannot help inlining

There is an outdated rule that "final keywords are used extensively in java getter and setter methods to support inlining," such as

public final String getUserName(){
	return this.userName;f
}

In Java, final keywords can be used to modify classes, methods and variables. In Chapter 3, we learn that final modifies variables in concurrency with the same semantics as volatile, memory visibility and no reordering. When final modifies a method, it means that the subclass cannot override the method. In early Java versions, final keywords also alerted virtual inlining, but now whether or not it is inlining does not require the use of final keywords. In his paper, Aleksey verifies that final keywords are not directly related to inlining methods. The Black Magic of (Java) Method Dispatch If you are interested in it, you can take a look at it for yourself. There is also a JMH project that you can download to test and analyze.

For inlining, refer to Chapter 8 JIT optimization

4.22.2 subString Memory Leakage

Earlier Java performance optimization books pointed out that String.subString is prone to memory leaks. The main reason is that JDK version of the subString method reuses the String array and does not generate a new char array for the new string. Thus, if the original String is particularly long, the space occupied by the newly returned String will also be Especially large. After JDK6, instead of multiplexing the char array when subString is used, it is replaced by regenerating an array. In Chapter 2, we see that constructing a String will regenerate a string array value.

private final char value[];
public String(char value[], int offset, int count) {
  	.....
    this.value = Arrays.copyOfRange(value, offset, offset+count);
}

4.22.3 Cycle Optimization

There is a saying that in nested loops, nested loops should follow the principle of "small inside big", so that the performance will be high. This statement gives two examples, the first is big outside small inside small.

stratTime = System.nanoTime();
for (int i = 0; i < 100_000_00; i++) {
	for (int j = 0; j < 10; j++) {

	}
}
endTime = System.nanoTime();
System.out.println("External large and internal small time-consuming:"+ (endTime - stratTime));

Small outside and big inside

stratTime = System.nanoTime();
for (int i = 0; i <10 ; i++) {
	for (int j = 0; j < 10000000; j++) {
	}
}
endTime = System.nanoTime();
System.out.println("External small and internal large time-consuming:"+(endTime - stratTime));

The two code fragments loop the same number of times, but the test results show that the latter is much larger than the former, that is, the performance of nested loop is higher than that of outer loop and inner loop.

This statement ignores that JIT will do DEAD-CODE elimination. JIT judges that the loop will not have any effect on the program and eliminates the loop body, resulting in inaccurate test results. If you use JMH test, as follows, you will find that nested loop results are the same.

//ForDeadCodeTest.java
@Benchmark
public long test(Blackhole hole) {
  long startTime = System.nanoTime();
  int i=0,j=0;
  for ( i = 0; i < 100_000_00; i++) {
    for ( j = 0; j < 10; j++) {
      hole.consume(j);
    }
    hole.consume(i);
  }

  Long endTime = System.nanoTime();
  return endTime-startTime+i+j;
}


@Benchmark
public long tes2(Blackhole hole) {
  long startTime = System.nanoTime();
  int i=0,j=0;
  for ( i = 0; i <10 ; i++) {
    for ( j = 0; j < 100_000_00; j++) {
      hole.consume(j);
    }
    hole.consume(i);
  }
  Long endTime = System.nanoTime();
  return endTime-startTime+i+j;
}
//Test benchmark, empty function
@Benchmark
public long base(Blackhole hole) {
  long startTime = System.nanoTime();

  Long endTime = System.nanoTime();
  return endTime-startTime;
}

The Blackhole function provided by JMH is used to prevent code elimination. This test finally proves that there is no difference between the performance of the two loops.

Benchmark                       Mode     Score    Units              
c.i.c.c.ForDeadCodeTest.base    avgt     0.000    ms/op              
c.i.c.c.ForDeadCodeTest.tes2    avgt    56.007    ms/op              
c.i.c.c.ForDeadCodeTest.test    avgt    50.228    ms/op              

Catching anomalies in a 4.22.4 loop

One theory is that abnormalities should be caught in vitro rather than in vivo, because try catch costs more.

for(int i=0;i<size;i++){
  try{

  }catch(Exception ex){

  }
}

As shown in the above code, this statement is suggested to be changed to circulating in vitro.

try{
  for(int i=0;i<size;i++){

  }
}catch(Exception ex){
}

In fact, the use of try catch in the circulating body has little effect on performance, so it is not necessary to pay attention to the need for circulating in vivo and in vitro. Just use try catch correctly according to your business needs.

Posted by nielst on Sun, 21 Jul 2019 20:30:03 -0700