The phenomenon of

java.lang.ArrayIndexOutOfBoundsException: null
Copy the code

A colleagues found abnormal production printing no stack, like the one above, only exception stack, is not convenient to troubleshoot problems, at first I thought is to use System. Print out, eventually found to be caused by OmitStackTraceInFastThrow finally

The code is reproduced as follows:

 public static void main(String[] args) {
        for (int i = 0; i < 1000 * 1000; i++) {
            try {
                t();
            } catch (Exception e) {
                if (null! = e.getStackTrace() && e.getStackTrace().length <=0) {
                    LOGGER.info("exception {}, {}", i, e);
                    return; }}}}public static void t(a) {
        int[] nums = new int[1];
        int m = nums[4];
    }
Copy the code

OmitStackTraceInFastThrow

This is an optimization made by HotSpot VM specifically for exceptions. It is enabled by default, and HotSpot Server Compiler (C2) uses Fast when exceptions are thrown many times in a particular place in the code Throw to optimize where the exception is thrown, directly throwing a preallocated object of type matching, whose message and stack trace are cleared.

The above is about online OmitStackTraceInFastThrow explanation, the amount of information is very large, we detailed analysis.

  1. Only for HotSpot VM, such as oracleJDK, libericaJDK, etc
  2. When a particular location is thrown many times, the JIT optimizes it
  3. The JIT must use C2 for this optimization. Instead of throwing the original exception, throw fast instead
  4. This is a pre-allocated exception, and both message and stack are empty

As you can see, if an exception is thrown in the same position for many times, will be the JIT C2 optimization into empty exception, for example, this article ArrayIndexOutOfBoundsException, no message, no stack. But it’s very fast and doesn’t have to allocate memory and fetch stacks.

There are also disadvantages, when you need to know where the problem is, you can’t see the stack, which is not conducive to troubleshooting.(Of course, if the log is saved for easy retrieval, you can also find the stack through the previous log)

If you want to close the optimization Settings – XX: – OmitStackTraceInFastThrow can

C1 C2 compiler

What is the C2 compiler mentioned above? In the paragraph here to extract an article www.cnblogs.com/death00/p/1…

JIT

When the JVM is initialized, the execution engine converts the bytecode to machine code during the execution of the class call, which can then be executed in the operating system. There is also compilation in the virtual machine during the bytecode conversion to machine code, which is the just-in-time compilation JIT

Initially, bytecode in the JVM is compiled by the Interpreter, and the virtual machine identifies a method or block of code as hot code when it detects that it is being run particularly frequently.

To improve the efficiency of hot code execution, the just-in-time compiler (JIT) compiles the code into local platform-specific machine code at runtime, optimizes it at various levels, and saves it In memory.

C1 compiler

C1 Compiler is a simple and fast Compiler, which mainly focuses on local optimization. It is suitable for programs with short execution time or requirements on startup performance, also known as Client Compiler. For example, GUI applications have certain requirements on interface startup speed.

C2 compiler

The C2 Compiler is a Compiler that performs performance tuning for long-running server-side applications. It is suitable for programs with long execution times or peak performance requirements, also known as Server Compiler. For example, long-running Java applications on the Server have certain requirements for stable running.

Layered compilation

Prior to Java7, the JIT was selected based on the characteristics of the program, and the virtual machine used an interpreter and one of the compilers by default.

Java7 introduces layered compilation, which combines the startup performance advantages of C1 with the peak performance advantages of C2, and we can also enforce the just-in-time compilation mode of the virtual machine with the -client or -server parameters.

Hierarchical compilation divides the JVM’s execution state into five levels:

  • Layer 0: program interpretation execution, performance monitoring is enabled by default, if not enabled, the second layer of compilation can be triggered;
  • Layer 1: called C1 compilation, bytecode is compiled into native code for simple, reliable optimization without Profiling turned on;
  • Layer 2: also known as C1 compilation, Profiling is turned on and only C1 compilation is performed with number of method calls and number of loop back side executions Profiling;
  • Layer 3: also known as C1 compilation, all C1 compilation with Profiling is performed;
  • Layer 4: CALLED C2 compilation, it also compiles bytecode to native code, but with some optimizations that take longer to compile, and even some aggressive optimizations that are unreliable based on performance monitoring information.

For the three states of C1, the execution efficiency is from high to low: layer 1, Layer 2, and layer 3.

C2 is typically over 30% more efficient than C1.

In Java8, hierarchical compilation is enabled by default, and the -client and -server Settings are no longer valid. -xx: -tieredCompilation can be turned off if you want to enable only C2. If you want to enable only C1, you can open hierarchical compilation at the same time with the argument: -xx :TieredStopAtLevel=1.

The JVM parameter

In order to observe the values mentioned above, we need to verify that they are valid with the JVM parameters. Three common parameters are provided below

  1. -XX:+PrintFlagsInitialThis parameter displays all settable parameters and their values before processing the parameters, and then exits the program directly
  2. -XX:+PrintCommandLineFlagsThis parameter displays all parameters and their values that differ from the initial default values after VM initialization
  3. -XX:+PrintFlagsFinal: Displays the values of the JVM parameters after initialization

validation

The JDK used for validation is libericaJDK

java -Xint -jar xxx.jar

Running in Interpreter mode (with C1, C2 off) is equivalent to -xx: -usecompiler and cannot reproduce the problem

java -XX:TieredStopAtLevel=1 -jar xxx.jar

Using only the C1 compiler, and staying at layer 1, cannot reproduce the problem

In particular, SpringBoot is selected by default for IDEAEnable launch optimizationTo optimize the startup speed, also resulting in the inability to reproduce the problem

java -XX:TieredStopAtLevel=4 -jar xxx.jar

Use C1,C2 compilers and stay at layer 4, which is also the default, to steadily reproduce the problem around 110000 times

java -XX:-TieredCompilation -jar xxx.jar

Disable layered compilation (that is, C2 only) for stable CompileThreshold after 10000 cycles and change -xx :CompileThreshold=200000 for stable CompileThreshold after 20000 cycles

java -XX:-OmitStackTraceInFastThrow -jar xxx.jar

Turn off the fast throw optimization and it won’t reappear anyway.

conclusion

Two requirements must be met to OmitStackTraceInFastThrow to take effect

  1. OmitStackTraceInFastThrow=true
  2. C2 compilerTo take effect

If encounter this kind of situation in the production, as far as possible to find the past log locking stack, behind production also can close OmitStackTraceInFastThrow