At the end of the article, the JVM mind map, if necessary to get their own

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

Let me say that the execution process is:

  1. The T1 and T2 threads are always summing up num
  2. The main thread sleeps for 1 second and wakes up after 1 second to print the num value at this time
  3. The T1 and T2 threads continue to add one until they have completed the 200 million summation operations

Do you agree?

My guess seemed fine, but I was proved wrong. Here’s the action diagram:

As you can see from the action diagram, after running the code, the actual execution result looks something like this:

After 1 second, the main thread does not print num immediately. Instead, it prints the value of num after T1 and T2 have each performed 200 million accumulates and exited the loop.

The result was not as expected. I run it based on JDK1.8, you can try it.

Why is that?

The answer is:

The JVM wants to perform an operation to get all the threads to the safe point, but the T1 and T2 threads are waiting for the loop to finish, so the main thread is waiting for T1 and T2 to print num.

For (int I = 0; i < 100000000; i++) {… } is called a countable cycle

Simply put, the main thread is waiting for the T1 and T2 threads to reach a safe point

This is the source of the answer, why god reprinted an article: “really amazing! This piece of code was manipulated by the JVM!” “Has already said very clearly, here will not repeat the elaboration.

This post comes from a question I had at the time: what is it that the JVM is doing to make threads safe?

Is there a GC?

Is there a GC happening?

First, the code does not create an object to apply memory.

Second, adding -XX: -printgc also does not print the GC log.

Third, you execute the jstat command, and you can see from the output log that no memory areas have changed during the JVM run, and no GC has occurred.

So, the need to enter a safe point because a GC has occurred is ruled out.

The question then becomes: Why do you need all threads to get to the safe point without GC happening?

Safety point log

Add the -XX:+ PrintSafePointStatistics parameter to cause the program to print the relevant log of the safe point while executing.

As you can see, this code executes three times to get to the security point.

The second EnableBiasedLocking is the JVM delay opening bias locking operation, which is also interesting, but is not the focus of this article and will be discussed next time.

Our focus is on the first No VM operation. Take this log out separately and add Chinese explanation on the parameter description:

To sum up:

The JVM wants to perform no VM operation, which requires all threads to enter the safe point. During this operation, there are 12 threads, and there are 2 threads running. It takes 5037 milliseconds to wait for these two threads to enter the safe point and then block.

It’s easy to find these two threads, but it takes more than 5000 milliseconds to reach the safe point. I’ll just add a parameter that allows the thread to timeout if the safe point takes more than 5000 milliseconds.

-XX:+ safePointTimeout and -XX: safePointTimeoutDelay =5000 execute the code.

Oh clear, this is T1 and T2 threads.

This result is also expected. Our focus is on what is this no VM operation? Why is the main thread waiting so long?

Source localization

The name of this VM operation is no VM operation, which means that it is not a VM operation.

How can an operation that is not a VM operation bring global access to a safe point?

So what is that operation? Knowledge is blind!

A Google Baidu, also did not find a more convincing answer.

So I decided to look at the JVM source code.

A global search for no VM operation in the JVM source code shows that only safepoint.cpp has this information.

Click to have a look, sure enough, positioning to print log, it is this SafepointSynchronize: : print_statistics () method.

Here is a key line of code:

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

This is a trinomial operation: if _vmop_type is equal to -1, the column for the safe point day operation type printed will print NO VM operation.

The _vmop_typen, in turn, is a member of the safePointStats structure, which refers to the type of VM operation that triggers the safe point.

What type of operation sets _vmop_type to -1?

I found the answer in the Open Safe Point method:

If it were not a safe point event triggered by a VM operation, the _VMOP_TYPE would be set to -1 at this point.

That is, there are other situations in which the safe point event can be triggered to allow all threads to enter the safe point.

Then, all we need to do is find the code that triggers the safety point event.

One file to find too difficult, another way of thinking, want to enter the safe point, must call into the safe point method.

Into the safer way is safepoint. The inside of the CPP SafepointSynchronize: : the begin () method.

Global search where we only need to call the SafepointSynchronize: : the begin () fire safety point event this method should be to find the corresponding code.

The global search found that only vmThread.cpp contains VMThread.cpp, which wraps VMThread-related methods.

VMThread

What is a VMThread?

VMThread is an internal thread started by the JVM itself, which is used to coordinate other threads to reach safe points and perform VM operations.

The concept of VM operations has been mentioned many times in this article, but what operations are VM operations?

We are familiar with CMS initial and final tokens that are VM operations, such as thread dump, thread suspension, and biased lock retraction.

There are many types of VM operations, and the source code for the JVM is in the macro VM_OPS_DO defined by vm_operations.hpp.

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

VMThread contains a VMOperationQueue queue, which is used to hold VM operations that are linked together.

The method that VMThread loops through to perform VM operations is called the VMThread::loop() method.

The loop() method is the core method of VMThread, which constantly retrievesVM operations to be executed from the VMOperationQueue queue, and then calls the evaluate() method, a specific implementation of each VM operation, to execute a different logic.

The policy pattern is used here, where the VMThread execution logic is fixed and only responsible for scheduling, and each VM operation needs to implement the evaluate() method itself as required.

The answer appears

The reason we are looking for no VM operation is in the loop() method of VMThread.

As can be seen from the source code, if the VM operation is empty, as long as the following three conditions are met, the security point will also be reached:

  1. VMThread is up and running
  2. The interval to the safe point is designed
  3. Whether safePointalot is true or whether it needs to be cleaned

VMThread must run properly, so condition 1 is satisfied.

In Java – XX: + UnlockDiagnosticVMOptions – XX: + PrintFlagsFinal 2 > &1 | grep Safepoint command to view the JVM default arguments about safety point, Found GuaranteedSafepointInterval default setting is 1 second, so can meet the condition 2.

SafePointalot defaults to false for condition 3. SafePointSynchronize ::is_cleanup_needed() must be true for condition 3 to be satisfied.

Click on it to see the implementation:

By tracing the code, SafepointSynchronize::is_cleanup_needed() determines whether there is a stub cache in the StubQueue.

So what is the Stubqueue? What’s a stub?

This involves the template interpreter and compiler for the JVM, but space is limited, so I’ll discuss this in more detail next time.

I’ll summarize it in one sentence as a cache of compile-interpreted code during JVM execution.

Cleaning stubs can simply be understood as cleaning the code cache.

That is, while the JVM is running normally, if the interval to the safe point is set, it will be determined at regular intervals if there is any code cache to clean up, and if so, the safe point will be entered.

This trigger condition is not a VM operation, so the _VMOP_TYPE is set to -1, and the corresponding No VM operation is printed when the log is printed, which we see as the safe point log.

The execution effect at the beginning of this article is that the main thread is waiting for T1 and T2 to enter the safe point, which triggers this condition.

Verify the Inference Again

Turned to look at the beginning of the article code, by adding – XX: GuaranteedSafepointInterval = 0 will enter a safer time interval is set to 0, which is close timing into safe point, take a look at what is the code to run results.

– XX: GuaranteedSafepointInterval are the parameters in the diagnosis of nature, the need to add – XX: + UnlockDiagnosticVMOptions unlock diagnostic parameters before use.

As can be seen from the results, after closing for a period of time and entering the safe point setting, the main thread does not need to wait for T1 and T2 threads to finish executing in the loop for 1 second, and immediately after sleeping, the num value of this time is printed.

The result of this operation again verifies our inference.

The one-second access to safety point setting still has its effect, and I recommend you leave it alone.

– XX: GuaranteedSafepointInterval is a diagnostic properties parameters, not recommended for online use.

According to the literature on the Internet, turning off this parameter may cause some unknown errors. I have not encountered any specific errors, and I do not know whether they are true or false.

In summary, it’s always a good idea to be cautious about online environments, and if you’re not familiar with the underlying JVM, I recommend leaving it alone.

Interesting comment

This is the end of the knowledge sharing, to share an interesting thing.

While tracing the source code of the JVM, I found that the StubQueue author had left a comment like this:

Don’t fiddle with my code if you can’t prove it’s working. It’s much better than you think.

See, this is the pride and confidence of the great god!

In contrast, when I usually write code comments, I only dare to write: if you see my code has a BUG, please help me fix it, thank you.

The pride and confidence with which I write notes shows how far I have come from God.

I must refuel, later also can write such a domineering annotation!

Mind mapping

I’ve put together a mind map of what I think is important about the JVM.

There is a need to be able to take on the line, if the picture is compressed platform, you can public number back to the JVM background to get high-definition pictures.

It should be emphasized that this is the knowledge point I organized, and the knowledge in it is not my original.

I didn’t create knowledge, I just shared how I learned and understood it.

A number of books and blogs have been consulted for the creation of mind maps, including but not limited to Understanding the Java Virtual Machine in depth, Meituan technical team articles, Ali technical team articles, R big articles, Hanquan big tuning articles.

Well, that’s all for today’s article.

I’m Coderw, a programmer who sometimes likes to get into trouble. See you next time!