5
头图
JVM mind map at the end of the article, if you need it

If you are familiar with concurrent programming, what do you think of the execution result of the following code?

If I say, the execution process is:

  1. The t1 thread and t2 thread have been performing the accumulation operation of num
  2. The main thread sleeps for 1 second, and wakes up after 1 second to print the value of num at this time
  3. The t1 thread and the t2 thread continue to perform the operation of adding 1 until the 200 million accumulation operations are performed

Do you approve

My guess seems to be okay, but the actual running effect proved me wrong. The following is the running animation:

As you can see from the running animation, after running the code, it turns out that the actual execution result is like this:

1 second, the main thread did not print num immediately, but waited for t1 and t2 to perform 200 million accumulation operations and exit the loop before printing the value of num .

This result is not the same as expected. I ran it based on JDK1.8, you can also try it.

Why is this so?

the answer is:

JVM wants to perform an operation to allow all threads to enter a safe point, but the t1 and t2 threads must wait for the loop to complete before entering the safe point because of the JIT transition optimization of countable loops, so the main thread has been waiting for t1 and t2. The value of num cannot be output for a long time.

Countable loops: loops of the form for (int i = 0; i <100000000; i++) {...} are called countable loops

Simply put: main thread is waiting for the t1 and t2 threads to enter the safe point

The origin of this answer is an article reprinted by why God: "It's amazing! of code that was touched by the JVM! "160a1ff6c5110f has already stated very clearly, so I won’t repeat it here.

This article originated from a question I had at the time: What the unknown thing did the 160a1ff6c5112d JVM do to make the threads enter the safe point?

GC happened?

Did GC happen?

First, there is no object created in the code to request memory.

Second, adding -XX:-PrintGC also did not print the GC log.

Third, execute the jstat command, and it can be seen from the output log that there is no change in each memory area during the JVM operation, and no GC occurs.

Therefore, the need to enter a safe point because of a GC is ruled out.

The question then becomes: no GC occurs, what do you need all threads to do to enter a safe point?

Security point log

Add the -XX:+PrintSafepointStatistics parameter to print the relevant log of the safepoint when the program is executed.

It can be seen that the execution of this code has been carried out three times to enter the safe point.

Among them, the second EnableBiasedLocking is the operation of JVM to delay the opening of the biased lock. This is also more interesting, but it is not the focus of the article. I will talk about it next time.

Our focus is on the first no vm operation operation. Take out this log separately and add a Chinese explanation to the parameter description:

In summary:

JVM wants to execute no vm operation . This operation requires threads to enter the safe point. There are 12 threads in total during the whole period, and there are 2 running threads. You need to wait for these two threads to enter the safe point and wait for these 2 threads to enter It took 5037 milliseconds to safely click and block.

It is also very simple to find these two threads. Doesn't it take more than 5000 milliseconds to enter the safe point? I just add parameters to make the thread that enters the safe point more than 5000 milliseconds timeout.

So add -XX:+SafepointTimeout and -XX:SafepointTimeoutDelay=5000 parameters to execute the code.

Oh, isn't this the t1 and t2 threads?

This result is also expected. Our focus is what exactly is no vm operation Why make the main thread wait so long?

Source code positioning

The name of this VM operation is called no vm operation , which translates to Chinese is is not a VM operation . If it is connected, it is a VM operation that is not a VM operation?

An operation that is not a VM operation can actually bring the whole world into a safe point?

What exactly is the operation? Knowledge blind zone!

Even Google Baidu did not find a more convincing answer.

Ever since, I decided to look at the source code of the JVM.

no vm operation globally in the JVM source code, only safepoint.cpp has this information.

Click to take a look, and sure enough, to locate the print log at once, this is the SafepointSynchronize::print_statistics() method.

There is a very key code:

_vmop_type == -1 ? 
    "no vm operation" : 
    VM_Operation::name(sstats->_vmop_type)

no vm operation will be output in the column of the operation type of the security point printed.

And this _vmop_typen is a member of the structure SafepointStats, the specific meaning is triggers the VM operation type safe point.

So what type of operation will set _vmop_type to -1?

I found the answer in the method of opening the safe point:

If it is not a security point event triggered by a VM operation, _vmop_type will be set to -1 at this time.

In other words, there are other situations that can also trigger a safe point event, allowing all threads to enter the safe point.

Then, we only need to find the code that triggers the safety point event.

It is too difficult to find files one by one. If you want to enter a safe point, you must call the method to enter the safe point.

The way to enter the safe point is the SafepointSynchronize::begin() method in safepoint.cpp.

We only need to search globally where the SafepointSynchronize::begin() method is called should be able to find the code that triggers the safepoint event.

Global search found that only vmThread.cpp has calls, and vmThread.cpp encapsulates all methods related to VMThread.

VMThread

What is VMThread?

VMThread is an internal thread started by the JVM itself. It is mainly used to coordinate other threads to reach a safe point and perform VM operations.

The concept of VM operations has been mentioned many times in the full text, so what operations are VM operations?

The initial mark and final mark of the familiar CMS are all VM operations. For example, thread dump, thread suspension, and cancellation of biased locks are all VM operations.

There are many types of VM operations. The source code corresponding to JVM is in the macro VM_OPS_DO defined in vm_operations.hpp.

Each VM operation in the macro VM_OPS_DO is basically implemented by a separate subclass.

There is a VMOperationQueue in VMThread, which is used to store VM operations connected one by one.

The method for VMThread to execute VM operations in a loop is called the VMThread::loop() method.

The loop() method is the core method of VMThread. This method continuously obtains the VM operations to be executed from the VMOperationQueue queue, and then calls the evaluate() method of each VM operation to execute different logics.

The strategy mode is used here, the execution logic of VMThread is fixed, and it is only responsible for scheduling, and each VM operation needs to implement the evaluate() method by itself according to the needs.

The answer appears

The reason for the no vm operation we have been looking for is in the loop() method of VMThread.

As you can see from the source code, when the VM operation is empty, as long as the following three conditions are met, the safe point will also be entered:

  1. VMThread is in normal operation
  2. Designed the interval time to enter the safe point
  3. Is SafepointALot true or does it need to be cleaned up

If the program runs normally, VMThread will definitely run normally, so condition 1 can be met.

Use the java -XX:+UnlockDiagnosticVMOptions -XX:+PrintFlagsFinal 2>&1 | grep Safepoint command to view the JVM's default parameters on the safe point. It is found that the GuaranteedSafepointInterval is set to 1 second by default, so condition 2 can also be met.

For condition 3, SafepointALot is false by default. If condition 3 is to be met, SafepointSynchronize::is_cleanup_needed() must be true.

Click in to see its specific implementation:

By tracing the code, you can find that SafepointSynchronize::is_cleanup_needed() is to determine whether there is a stub cache in the StubQueue.

What is StubQueue? What is a stub?

This involves the JVM template interpreter and compiler. Due to the limited space, I will continue to discuss it if I have the opportunity next time.

I use one sentence to summarize that is the compiled and interpreted code cache during the execution of the JVM.

Clean up the stub You can simply understand it as clean up the code cache .

That is to say, when the JVM is running normally, if the interval for entering the safe point is set, it will be judged at intervals whether there is a code cache to be cleaned up, and if so, it will enter the safe point.

This trigger condition is not a VM operation, so _vmop_type will be set to -1, and the corresponding no vm operation will be printed when the log is output, which is the security point log we see.

As for the code execution effect at the beginning of the article, the main thread has been waiting for t1 and t2 to enter the safe point, which triggers this condition.

Verify the inference again

Looking back at the code at the beginning of the article, by adding -XX:GuaranteedSafepointInterval = 0 to set the interval of entering the safepoint to 0, that is, turning off the timing to enter the safepoint, and see how the code runs.

-XX:GuaranteedSafepointInterval is a diagnostic parameter, you need to add -XX:+UnlockDiagnosticVMOptions parameter to unlock the diagnostic parameter before it can be used.

It can be seen from the running results that after turning off the setting to enter the safe point for a period of time, after the main thread sleeps for 1 second, it is no longer necessary to wait for the t1 and t2 threads to execute the loop, and immediately print the num after sleeping value.

The results of this operation have once again verified our inferences.

The setting of entering the safe point at intervals of one second still has its effect, I suggest you do not move it.

-XX:GuaranteedSafepointInterval is a diagnostic parameter and is not recommended for online use.

Judging from the online literature, turning off this parameter may also cause some unknown errors. I have not seen the specific error, and I don't know whether it is true or false.

In short, it's always right to be cautious about the online environment. If you are not familiar with the underlying JVM, I suggest you don't touch it.

Interesting note

The sharing of knowledge points is over here, share an interesting thing.

In the process of tracing the JVM source code, I found that the author who wrote the StubQueue left a note like this:

Let me polish and translate it: When you can't prove that your changes are OK, don't mess with my code. This code is more than you think.

Seeing that, this is the pride and self-confidence of the great god!

On the other hand, when I write comments to the code, I only dare to write on it: If you see a bug in my code, please help me fix it, thank you .

From the pride and self-confidence in writing notes, you can see how far I am from the Great God.

I must cheer, and I can write such domineering comments in the future!

mind Mapping

I have organized the JVM knowledge points that I personally think are important into a mind map according to my own understanding.

it up by yourself. If the picture is compressed by the platform, you can return to 160a1ff6c51ba6 JVM get high-definition pictures.

It needs to be emphasized that this is the knowledge point I have compiled, and the knowledge inside is not my original.

I did not create knowledge, I just shared how I learned and understood knowledge.

The production of the mind map refers to a large number of books and blogs, including but not limited to "In-depth understanding of the Java virtual machine", Meituan technical team articles, Ali technical team articles, R big articles, and Hanquanzi's great tuning articles.

Well, that's the end of today's article.

I’m CoderW, a programmer who likes to be a bit tricky sometimes, see you next time!


CoderW
148 声望1.1k 粉丝