Hello, this is Why.

Do you know what I’m going to write about this time without eggs?

I’m sorry, I’m a little early, I didn’t give you any hints, so guess what, right?

Here is the last code:

public class ExceptionTest { public static void main(String[] args) { String msg = null; for (int i = 0; i < 500000; i++) { try { msg.toString(); } catch (Exception e) { e.printStackTrace(); }}}}

Come on, this code, you guess what to write a flower?

Of course, there are friends who guess and friends who don’t.

Good, so if you’ve figured it out, pull it to the end of the text, complete the three-key task, and then you can go out.

For those of you who haven’t guessed it, you’ll know what I’m going to say as soon as I run through the code:

It was a flash, did you see? Magic? Questions?

It doesn’t matter, if you don’t see it clearly, I can give you a screenshot:

After a certain number of null-pointer exceptions are thrown, the exception stack is gone.

That’s what my headline says: That’s ridiculous, right? All of a sudden the exception message is gone.

.png)

Why would you say?

Why?

The story began in 2004.

That year, Sun released JDK 5 at 18:00 on September 30.

In its release-notes there is a passage like this:

https://www.oracle.com/java/t…

Mainly is the box up this sentence, do not understand it does not matter, I use my eight and a half English to translate for you.

Let’s do this:

The compiler in the server VM now provides correct stack backtraces for all “cold” built-in exceptions.

For all built-in exceptions, the compiler can provide the correct traceback of the exception stack.

For performance purposes, when such an exception is thrown a few times, the method may be recompiled.

For performance reasons, the method may be recompiled after an exception is thrown several times. (important)

After recompilation, the compiler may choose a faster tactic using preallocated exceptions that do not provide a stack trace.

After recompilation, the compiler may choose a faster strategy of pre-allocated exceptions that do not provide an exception stack trace. (important)

To disable completely the use of preallocated exceptions, use this new flag: -XX:-OmitStackTraceInFastThrow.

If you want to ban the use of pre-allocated abnormal, please use the new parameters: – XX: – OmitStackTraceInFastThrow.

I don’t care if I understand these words. But at least you know that the scenario that it’s describing here isn’t the scenario that I just demonstrated in this code?

It mentions a parameter – XX: – OmitStackTraceInFastThrow, word, fetched in the first, and see the effect:

In the same code, the exception stack does print from start to finish with this startup parameter.

I don’t know if you feel this, but after adding this startup parameter, the application runs significantly slower.

I did not add this parameter on my machine, so the program running time is 2826 ms, and the running time is 5885 ms with this parameter.

Indicates that there are indeed performance enhancements.

How is it promoted? We’ll see in the next section.

Let’s talk about something else first.

With the JVM parameters mentioned, I’d like to share one more site:

https://club.perfma.com/topic…

The site offers many features, here are a few of them:

The JVM parameter query function must have:

Very easy to use, you do not know what is the use of the JVM parameters, you can query on this website.

Why on earth?

As mentioned earlier, for performance reasons, the exception stack is missing starting with JDK 5.

So what’s the performance problem?

Let’s take a look at the most common null-pointer exceptions.

As an example, take a look at the call path when an exception is thrown:

You’ll end up with this native method:

java.lang.Throwable#fillInStackTrace(int)

Fill In the Stack Trace, as the name implies, and fill In the Stack Trace.

This method climbs the stack, which is a relatively performance-intensive process.

Why is it taking so long?

Here’s an intuitive one:

This kind of exception stack is our more common, such a long stack information, can not cost performance.

Now, let’s go back to this sentence:

For performance purposes, when such an exception is thrown a few times, the method may be recompiled. After recompilation, the compiler may choose a faster tactic using preallocated exceptions that do not provide a stack trace.

For performance reasons, the method may be recompiled after an exception is thrown several times. After recompilation, the compiler may choose a faster strategy of pre-allocated exceptions that do not provide an exception stack trace.

So, as you can see, the phrase “for performance reasons” specifically refers to the cost of saving fillInstackTrace performance.

For a more in-depth comparison, you can check out this link:

http://java-performance.info/…

Here is my conclusion:

With regard to the performance cost of eliminating exceptions, he proposes three solutions:

Refactor your code to not use them.




Cache exception instances.




Override the fillInstackTrace method.


Through the little day… A Japanese website with a good life, ScienceOnline, after entering key information, this link from Zhihu ranked second:

https://www.zhihu.com/questio…

Below this question, there is an R big answer, paste it for you to see:

We’ve all talked about overwriting the fillInstackTrace method, which is a performance optimization trick where we can customize an exception like this:

To test it in a loose way, you can just look at the meaning:

We overwrote the fillInstackTrace method to return the object of this directly, which is not a bit faster than calling the original method of the stack climbing method.

In addition to overwriting the FillInstackTrace method, JDK 7 also provides a method like this:

java.lang.Throwable#Throwable(java.lang.String, java.lang.Throwable, boolean, boolean)

You can control whether you need to climb the stack by using writableStackTrace as an argument.

So when should you use such a performance optimization?

In fact, R’s answer is very clear:

In fact, we write business code, exception information printing is very necessary.

However, some performance-driven frameworks can take advantage of this.

Disruptor and Kafka, for example, are both sources that I found in their Disruptor source code.

First see Disruptor’s:

com.lmax.disruptor.AlertException

  • Overridden so the stack trace is not filled in for this exception for performance reasons.
  • For performance reasons, the overloaded stack trace will not be filled with this exception.

Let’s look at Kafka:

org.apache.kafka.common.errors.ApiException

  • avoid the expensive and useless stack trace for api exceptions
  • Avoid expensive and useless stack traces for API exceptions

And did you notice that synchronized is eliminated in both frames? If you’re going to rewrite it, you can also analyze whether synchronized can be removed from your scene and performance can be improved a bit.

In addition, R’s answer also mentioned that this optimization is the optimization of C2.

And we can prove it very easily.

Layered compilation

I mentioned C2 before, and I actually have a corresponding C1. C1 and C2 are just-in-time compilers.

If you’re not familiar with C1 and C2, let me put it differently.

C1 is actually a Client Compiler, that is, a client-side Compiler, which is characterized by short compilation time but low output code optimization.

C2 is actually the Server Compiler, namely the server-side Compiler, which is characterized by long compilation time and higher output code optimization quality.

Many of the “radical” performance optimizations that the JVM is often cited for, such as inlining, fast and slow path analysis, peephole optimizations, and, in this article, “showing no exception stacks,” are the work of C2.

By the way, in JDK 10, the Graal compiler was introduced, which was intended to replace C2.

As for why you want to replace C2, well, one of the reasons is this…

http://icyfenix.cn/tricks/202…

C2 has a long history, dating back to Cliff Click’s PhD work, and the C++ compiler, while still clustering, has become so complex that even Cliff Click himself is reluctant to maintain it.

You see what I said earlier about C1 and C1 is that they are complementary.

So in order to strike a balance between application startup, responsiveness, and application efficiency, the JVM has supported a pattern called tiered compilation since JDK 6.

That’s why people say, “Java code is getting faster and faster, and Java code needs to warm up.”

To give you a brief idea of what this is, I’ll refer to Section 7.2.1 [Layered Compilation] of the In-depth Understanding of the Java Virtual Machine Hotspot.

First, we can turn on TieredCompilation using -XX:+TieredCompilation, which introduces four additional compilation levels.

  • Level 0: Explain execution.
  • Level 1: C1 compilation, all optimizations turned on (without Profiling). Profiling is Profiling.
  • Level 2: C1 compilation, Profiling information with call counting and back edge counting (restricted Profiling).
  • Level 3: C1 compilation with all Profiling information (full Profiling).
  • Level 4: C2 compilation.

The common transformation path of hierarchical compilation level is shown in the figure below:

  • 0→3→4: Common hierarchy transition. It is fully compiled with C1, and then goes to level 4 if subsequent methods execute frequently enough.
  • 0→2→3→4: C2 compiler busy. It quickly compiles at level 2, then moves to level 3 when enough Profiling information is gathered, and finally to level 4 when C2 is no longer busy.
  • 0→3→1/0→2→ 1:2/3 becomes level 1 after compiling because the method is not important. It also goes to level 1 if C2 doesn’t compile.
  • 0→(3→2)→4: The C1 compiler is busy, and the compilation task can either wait for C1 or quickly go to level 2, and then from level 2 to level 4.

If you didn’t know about hierarchical compilation, that’s fine, now you have a concept like this. The interview will not be tested, don’t worry.

Next, there is a parameter:

-XX:TieredStopAtLevel=___

This parameter stops the layered compilation at a certain level. The default value is 4, which is to compile to C2.

If I change the value to 3, I can only use C1, so I can’t use C2 to optimize the exception for me.

Experiment 1:

Sure enough, R didn’t cheat me.

This is a brief introduction to layered compilation.

Knowledge is very big, you can go to study if you are interested.

The above.