Hello, dear friends, technology lei brother, no progress to say! Welcome to the new performance Reading series, I’m Lei.

Today’s article is on whether a try-catch should be outside the loop or inside the loop, and we’ll answer that question in terms of both performance and business scenario analysis.

In this article, we will explore the nature of a try-catch. For example, we often equate try-catch with “low performance.”

Tip: I will try to use the code and the evaluation results to prove the problem, but due to my own cognitive limitations, if there is any improper, please point out in the comments section of readers.

The performance evaluation

Without further discussion, we will start today’s test directly. In this article, we will still use the Java Microbenchmark Harness (JMH) provided by Oracle.

First add the JMH framework to the POM.xml file as follows:

<! -- https://mvnrepository.com/artifact/org.openjdk.jmh/jmh-core --> <dependency> <groupId>org.openjdk.jmh</groupId> <artifactId>jmh-core</artifactId> <version>{version}</version> </dependency>Copy the code

The complete test code is as follows:

import org.openjdk.jmh.annotations.*; import org.openjdk.jmh.runner.Runner; import org.openjdk.jmh.runner.RunnerException; import org.openjdk.jmh.runner.options.Options; import org.openjdk.jmh.runner.options.OptionsBuilder; import java.util.concurrent.TimeUnit; // @benchmarkMode (mode.averageTime) // @outputTimeUnit (timeunit.nanoseconds) @warmup (iterations = 1, time = 1, timeUnit = timeunit. SECONDS) // Go to Warmup one more time. Iterations = 5, time = 5, timeUnit = timeunit.seconds) // Perform 5 iterations. @state (scope.benchmark) @threads (100) public class TryCatchPerformanceTest {private static  final intforSize = 1000; Public static void main(String[] args) throws RunnerException {// Start the benchmark. Options opt = new OptionsBuilder() Include (TryCatchPerformanceTest. Class. GetSimpleName ()) / / to import the test class. The build (); new Runner(opt).run(); } @benchmark public intinnerForeach() {
        int count = 0;
        for (int i = 0; i < forSize; i++) {
            try {
                if (i == forSize) {
                    throw new Exception("new Exception"); } count++; } catch (Exception e) { e.printStackTrace(); }}return count;
    }

    @Benchmark
    public int outerForeach() {
        int count = 0;
        try {
            for (int i = 0; i < forSize; i++) {
                if (i == forSize) {
                    throw new Exception("new Exception");
                }
                count++;
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        returncount; }}Copy the code

The test results of the above code are:

As can be seen from the above results, the average single execution time of the program in the case of 1000 cycles is:

  • The average execution time of a loop with a try-catch is 635 nanoseconds ±75 nanoseconds, which means that the upper and lower error is 75 nanoseconds;
  • The average execution time outside the loop with a try-catch is 630 nanoseconds, with an up/down error of 38 nanoseconds.

That is, in the absence of exceptions, minus the error value, we conclude that try-catch performance is the same inside and outside the for loop, with almost no difference.

The essence of the try-catch

To understand the performance problem of a try-catch, I have to start with its bytecode analysis. Only then can I understand the nature of a try-catch and how it is executed.

Here we write the simplest try-catch code:

public class AppTest {
    public static void main(String[] args) {
        try {
            int count = 0;
            throw new Exception("new Exception"); } catch (Exception e) { e.printStackTrace(); }}}Copy the code

After generating the bytecode using javac, use the javap -c AppTest command to view the bytecode file:

➜ javap -c AppTest Compiled from warning: The binary file AppTest contains com.example.AppTest Compiled from"AppTest.java"
public class com.example.AppTest {
  public com.example.AppTest();
    Code:
       0: aload_0
       1: invokespecial #1 // Method java/lang/Object."
      
       ":()V
      
       4: return

  public static void main(java.lang.String[]);
    Code:
       0: iconst_0
       1: istore_1
       2: new           #2 // class java/lang/Exception
       5: dup
       6: ldc           #3 // String new Exception
       8: invokespecial #4 // Method java/lang/Exception."
      
       ":(Ljava/lang/String;) V
      
      11: athrow
      12: astore_1
      13: aload_1
      14: invokevirtual #5 // Method java/lang/Exception.printStackTrace:()V
      17: return
    Exception table:
       from    to  target type
           0    12    12   Class java/lang/Exception
}
Copy the code

As you can see from the bytecode above, there is an exception table:

Exception table:
       from    to  target type
          0    12    12   Class java/lang/Exception
Copy the code

Parameter Description:

  • From: indicates the start address of a try-catch.
  • To: indicates the end address of a try-catch.
  • Target: indicates the start bit of exception processing.
  • Type: indicates the name of the exception class.

As can be seen from bytecode instructions, when the code runs with an error, it will first determine whether the error data is in the range of from to, if so, it will execute from the target flag down, if there is no error, directly goto to return. That is, if the code doesn’t go wrong, performance is almost unaffected, just like normal code execution logic.

Business analysis

While try-catch performance is similar in and out of the loop, the business implications of the code are completely different, such as the following code:

public class AppTest {
    public static void main(String[] args) {
        System.out.println("Execution result within loop:" + innerForeach());
        System.out.println("Out-of-loop execution result:"+ outerForeach()); } // public static intinnerForeach() {
        int count = 0;
        for (int i = 0; i < 6; i++) {
            try {
                if (i == 3) {
                    throw new Exception("new Exception"); } count++; } catch (Exception e) { e.printStackTrace(); }}returncount; } public static intouterForeach() {
        int count = 0;
        try {
            for (int i = 0; i < 6; i++) {
                if (i == 3) {
                    throw new Exception("new Exception");
                }
                count++;
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        returncount; }}Copy the code

The execution results of the above programs are as follows:

java.lang.Exception: new Exception

at com.example.AppTest.innerForeach(AppTest.java:15)

at com.example.AppTest.main(AppTest.java:5)

java.lang.Exception: new Exception

at com.example.AppTest.outerForeach(AppTest.java:31)

at com.example.AppTest.main(AppTest.java:6)

Execution result within the loop: 5

Out of loop execution result: 3

You can see that a try-catch inside the loop can continue the loop after an exception occurs; A try-catch outside the loop terminates the loop after an exception occurs.

So our decision on whether a try-catch should be placed in or out of a loop does not depend on performance (since performance is almost the same), but rather on the specific business scenario.

For example, we need to process a batch of data, and no matter which data in the set is faulty, it does not affect the normal execution of other groups. In this case, we can put try-catch in the body of the loop. When we need to calculate the total value of a set of data, as soon as one set of data is wrong, we need to terminate execution and throw an exception. In this case, we need to place try-catch outside the loop to execute.

conclusion

In this article, we tested try-catch performance in and out of the loop and found that they performed almost identically over many cycles. Then bytecode analysis shows that only when an exception occurs, the exception table will be compared for exception processing, while the execution of try-catch can be ignored normally. However, using a try-catch in or out of the loop makes a huge difference in the outcome of the program, so we should decide where to store a try-catch for practical business purposes, not performance considerations.

Follow the public account “Java Chinese community” reply “dry goods”, obtain 50 original dry goods Top list.