Network Security Internet Technology Development Database Servers Mobile Phone Android Software Apple Software Computer Software News IT Information

In addition to Weibo, there is also WeChat

Please pay attention

WeChat public account

Shulou

What is SafePoint and Stop The World

2025-03-31 Update From: SLTechnology News&Howtos shulou NAV: SLTechnology News&Howtos > Development >

Share

Shulou(Shulou.com)06/03 Report--

This article mainly explains "what is SafePoint and Stop The World". The content of the explanation is simple and clear, and it is easy to learn and understand. Please follow the editor's train of thought to study and learn "what is SafePoint and Stop The World".

When analyzing online JVM performance issues, we may encounter the following scenarios:

1.GC itself didn't take long, but JVM paused for a long time, such as the following:

2.JVM does not have GC, but the program has been suspended for a long time, and this happens from time to time.

These problems are generally related to SafePoint and Stop the World.

What is SafePoint? What is Stop the world? What's the relationship between them?

Let's first imagine the following scenario:

When you need GC, you need to know which objects are still in use or are no longer in use and can be recycled, so you need the object usage of each thread.

For biased locks (Biased Lock), unbiasing at high concurrency requires thread state and precise information about the thread that acquired the lock.

Immediate compilation optimization (replacement on the OSR stack) or anti-optimization (anti-optimization on the bailout stack) on the method requires information about exactly where the thread is running to the method.

For these operations, you need all kinds of information about the thread, such as what is in the register, heap usage information, stack method code information, and so on, and when doing these operations, the thread needs to be paused until these operations are completed, otherwise there will be concurrency problems. This requires SafePoint.

Safepoint can be understood as some special place during code execution, and when a thread executes to these locations, the thread can pause. Some running information of the current thread that is not available in other locations is saved in SafePoint for other threads to read. This information includes any information about the thread context, such as internal pointers to objects or non-objects, and so on. We generally understand SafePoint in this way, that is, only when a thread runs to the location of the SafePoint, all its state information is determined, and only at this time can we know what memory the thread uses and what it does not use. Moreover, only when the thread is in the SafePoint position, the stack information of the JVM is modified, such as reclaiming some unused memory, the thread will perceive it, and then it will continue to run, and each thread will have its own snapshot of memory use. at this time, the thread will not know about the changes to the memory usage of other threads, and it will only be aware of it when it comes to SafePoint.

Therefore, GC must require all threads to enter SafePoint at the same time and stay there, waiting for GC to finish processing memory before all threads continue to hold on. A situation like this when all threads enter the SafePoint * * and wait is Stop the world (at this point, I suddenly think of Seung Taro's: the world sauce thrown in the cafeteria! ).

Why do you need SafePoint and Stop The World?

Everything in the thread context, including objects, internal pointers to objects or non-objects, is saved at the SafePoint location, and this information is modified when the thread is in SafePoint so that the thread can be aware of it. Therefore, it is only when the thread is in SafePoint that you can GC the memory used by the thread and change the code that is executing, such as OSR (On Stack Replacement, replacing existing code with JIT optimized code on the stack) or Bailout (replacing JIT optimized code on the stack to de-optimized code). Also, there is another important Java threading feature that is also implemented based on SafePoint, which is Thread.interrupt (), where threads don't know whether they are interrupted until they run to SafePoint.

Why do we need Stop The World? sometimes we need all threads globally to enter SafePoint in order to count which memory can also be reclaimed for GC, and code that is no longer used to clean CodeCache, and to execute some Java instrument commands or JDK tools, such as jstack printing stack requires Stop the world to take a snapshot of all current threads.

How is SafePoint implemented?

It can be understood that SafePoint can be inserted somewhere in the code, and when each thread runs into the SafePoint code, it takes the initiative to check whether it needs to enter SafePoint. This active checking process is called Polling.

In theory, it is possible to place a machine command that checks Safepoint at the boundary of each Java-compiled bytecode. When the thread executes here, it executes Polling to ask JVM if it needs to enter SafePoint, which is a performance penalty, so JIT optimizes to minimize SafePoint.

Code optimized by JIT compilation places a SafePoint before the return of all methods and before all non-counted loop loops (unbounded loops) bounce back. To prevent the occurrence of GC requiring Stop the world, the thread cannot be paused, but for explicitly bounded loops, in order to reduce SafePoint, a SafePoint is not placed before the bounce, that is:

For (int I = 0; I < 100000000; iTunes +) {.}

There is no SafePoint in it, which leads to some performance optimization problems that will be mentioned later. Note that only for int bounded loops, for example, if int I is changed to long I, there will still be SafePoint.

SafePoint implementation related source code: safepoint.cpp

As you can see, there are five cases of threads for SafePoint; suppose there is an operation that triggers an VM thread that all threads need to enter SafePoint (for example, GC is now needed), if other threads now:

Running bytecode: when running bytecode, the interpreter will see if the thread is marked as poll armed. If so, the VM thread calls SafepointSynchronize::block (JavaThread * thread) to block.

Run native code: when running native code, the VM thread skips this thread, but sets poll armed to the thread so that after it has executed the native code, it checks to see if it is poll armed, and if it still needs to stop at SafePoint, block directly.

Run JIT compiled code: since you are running compiled machine code, check directly to see if the local local polling page is dirty, and if so, you need block. After the introduction of JEP 312: Thread-Local Handshakes in Java 10, you only need to check whether the local local polling page is dirty.

In the BLOCK state: do not leave the BLOCK state until the operation that requires all threads to enter the SafePoint is completed

In thread switching state or VM running state: the thread state is polled until the thread is blocked (the thread is sure to become one of the four states mentioned above, which will block).

Under what circumstances will all threads be allowed to enter SafePoint, that is, Stop the world will occur?

Time to enter SafePoint: every time after the-XX:GuaranteedSafepointInterval configuration, all threads are allowed to enter the Safepoint, and as soon as all threads enter, they are immediately restored from the Safepoint. This timing is mainly for tasks that do not need to Stop the world immediately. You can set-XX:GuaranteedSafepointInterval=0 to turn off this timing, and I recommend turning it off.

Because commands such as jstack,jmap and jstat, most of the commands that the Signal Dispatcher thread processes, result in Stop the world: such commands need to collect stack information, so all threads need to enter the Safepoint and pause.

Biased lock cancellation (this does not necessarily trigger the overall Stop the world, see JEP 312: Thread-Local Handshakes): Java believes that locks are mostly uncompetitive (there is no simultaneous multithreading competition for locks in a synchronous block in most cases), so performance can be improved through bias. That is, when there is no competition, when the thread that previously acquired the lock acquires the lock again, it will determine whether the lock is biased towards me, then the thread will not have to acquire the lock again and can enter the synchronous block directly. However, in the case of high concurrency, the biased lock will often fail, resulting in the need to cancel the biased lock, and when canceling the biased lock, you need Stop the world, because you need to get the state and running status of each thread using the lock.

Agent loading and class redefinition caused by Java Instrument: because class redefinition is involved and the information related to this class on the stack needs to be modified, Stop the world is required.

Java Code Cache related: when JIT compilation optimization or de-optimization occurs, OSR or Bailout is required or code cache is cleaned, because you need to read the method executed by the thread and change the method executed by the thread, so Stop the world is required.

GC: this requires Stop the world because it requires object usage information for each thread, and reclaims some objects, frees up some heap memory or direct memory.

Some events of JFR: if OldObject collection of JFR is enabled, this is a regular collection of objects that have been alive for a long time, so Stop the world is required. At the same time, when JFR is in dump, because each thread has a buffer of JFR events, it needs to collect the events in buffer, so Stop the world is needed.

For other events that are not often encountered, you can refer to the source code vmOperations.hpp

# define VM_OPS_DO (template)\ template (None)\ template (Cleanup)\ template (ThreadDump)\ template (PrintThreads)\ template (FindDeadlocks) \ template (ClearICs)\ template (ForceSafepoint)\ template (ForceAsyncSafepoint)\ template (DeoptimizeFrame)\ template (DeoptimizeAll)\ template (ZombieAll)\ Template (Verify)\ template (PrintJNI)\ template (HeapDumper)\ template (DeoptimizeTheWorld)\ template (CollectForMetadataAllocation)\ template (GC_HeapInspection)\ template (GenCollectFull) \ template (GenCollectFullConcurrent)\ template (GenCollectForAllocation)\ template (ParallelGCFailedAllocation)\ template (ParallelGCSystemGC)\ template (G1CollectForAllocation)\ template (G1CollectFull)\ template (G1Concurrent)\ template (G1TryInitiateConcMark) \ template (ZMarkStart)\ template (ZMarkEnd)\ template (ZRelocateStart)\ template (ZVerify)\ template (HandshakeOneThread)\ template (HandshakeAllThreads) \ template (HandshakeFallback)\ template (EnableBiasedLocking)\ template (BulkRevokeBias)\ template (PopulateDumpSharedSpace)\ template (JNIFunctionTableCopier)\ template (RedefineClasses)\ template (UpdateForPopTopFrame)\ template (SetFramePop) \ template (GetObjectMonitorUsage)\ template (GetAllStackTraces)\ template (GetThreadListStackTraces)\ template (GetFrameCount)\ template (GetFrameLocation)\ template (ChangeBreakpoints)\ template (GetOrSetLocal) \ template (GetCurrentLocation)\ template (ChangeSingleStep)\ template (HeapWalkOperation)\ template (HeapIterateOperation)\ template (ReportJavaOutOfMemory)\ template (JFRCheckpoint)\ template (ShenandoahFullGC) \ template (ShenandoahInitMark)\ template (ShenandoahFinalMarkStartEvac)\ template (ShenandoahInitUpdateRefs)\ template (ShenandoahFinalUpdateRefs)\ template (ShenandoahDegeneratedGC)\ template (Exit)\ template (LinuxDllLoad)\ template (RotateGCLog) \ template (WhiteBoxOperation)\ template (JVMCIResizeCounters)\ template (ClassLoaderStatsOperation)\ template (ClassLoaderHierarchyOperation)\ template (DumpHashtable)\ template (DumpTouchedMethods)\ template (PrintCompileQueue) \ template (PrintClassHierarchy)\ template (ThreadSuspend)\ template (ThreadsSuspendJVMTI)\ template (ICBufferFull)\ template (ScavengeMonitors)\ template (PrintMetadata)\ template (GTestExecuteAtSafepoint) \ template (JFROldObject)\ what causes the Stop the world time to be too long?

The Stop the world phase can be simply divided into (during this period, the JVM is because all threads enter the block state of the Safepoint):

An operation that requires Stop the world (that is, the situations mentioned above that allow all threads to enter the SafePoint, that is, the operations where Stop the world occurs)

A Safepoint synchronization signal is issued to the JVM daemon thread Signal Dispatcher and handed over to the corresponding module for execution.

The corresponding module collects all thread information and makes different operations and tags for each thread according to the state (according to the description of the previous source code, there are 5 cases)

All threads enter Safepoint and block.

Do the actions that need to initiate a Stop the world.

The operation completes and all threads resume from the Safepoint.

Based on these stages, the reasons for the long Stop the world time are:

Phase 4 takes too long, that is, it takes too long to wait for some of all threads to enter the Safepoint, which is probably related to the bounded loop and JIT optimization, the use of the class StackWalker introduced by OpenJDK 11 to get the call stack, or the system CPU resource problem or too many dirty pages in the system memory or swap.

Phase 5 takes too long, so we need to see which operations are caused, such as excessive undo of bias locks, long GC time, and so on. We need to find ways to reduce the time consumed by these operations, or directly turn off these events (such as turning off bias locks and OldObjectSample event collection of JFR) to reduce entry, which has nothing to do with this article and will not be discussed here.

Phase 2, phase 3 takes too long, because Signal Dispatcher is single-threaded, you can see what the Signal Dispatcher thread is doing at that time, which may be caused by other operations done by Signal Dispatcher. It may also be caused by a system CPU resource problem or too many dirty pages in the system memory or swap.

What problems will big bounded loops and JIT optimization bring to SafePoint?

Known: only the thread executes to the Safepoint code to know the latest state of Thread.intterupted (), not the thread's local cache.

Let's take a look at the following code:

Static int algorithm (int n) {int bestSoFar = 0; for (int iTun0; I {Instant start = Instant.now (); int bestSoFar = algorithm (1000000000); double durationInMillis = Duration.between (start, Instant.now ()). ToMillis (); System.out.println ("after" + durationInMillis+ "ms, the result is" + bestSoFar);}; / / delay interrupt Thread t = new Thread (task) after 1ms; t.start () Thread.sleep (1); t.interrupt (); / / delay interrupt t = new Thread (task) after 10ms; t.start (); Thread.sleep (10); t.interrupt (); / / delay interrupt t = new Thread (task) after delay 100ms; t.start (); Thread.sleep (100); t.interrupt () / / after a delay of 1 second, interrupt / / the number of for loop calls in the algorithm should be sufficient, and the code will be compiled and optimized immediately and OSR t = new Thread (task); t.start (); Thread.sleep (1000); / / it is found that the thread will not react to interrupt this time t.interrupt ();}

After that, use the JVM parameter-Xlog:jit+compilation=debug:file=jit_compile%t.log:uptime,level,tags:filecount=10,filesize=100M to print the JIT compilation log to another file for easy observation. Finally, the console output:

Broken by interruptedbroken by interruptedafter 10.0 ms, the result is 27after 1.0 ms, the result is 10broken by interruptedafter 99.0 ms, the result is 69after 29114.0 ms, the result is 501

As you can see, the last loop run is directly over, and you don't see that the thread has already interrupted. And the JIT compilation log can see that code instant compilation optimization and OSR occurs when the last thread executes the loop:

[0.782s] [debug] [jit,compilation] 460% 3 com.test.TypeTest::algorithm @ 4 (44 bytes) [0.784s] [debug] [jit,compilation] 468 3 com.test.TypeTest::algorithm (44 bytes) [0.794s] [debug] [jit,compilation] 486% 4 com.test.TypeTest::algorithm @ 4 (44 bytes) [0.797s] [debug] [jit Compilation] 460% 3 com.test.TypeTest::algorithm @ 4 (44 bytes) made not entrant [0.799s] [debug] [jit,compilation] 503 4 com.test.TypeTest::algorithm (44 bytes)

3 and 4 indicate the compilation level, and% indicates a replacement method on the OSR stack, that is, the machine code substitution that executes the code while the for loop is still executing. After that, the thread will not see that the thread has interrupted, which means that the Safepoint in the for loop will be removed from the * * JIT optimized code.

The problem is also obvious: when a Stop the world is needed, all threads wait for the loop to finish, because the thread cannot enter the Safepoint until it has finished executing the large loop.

So, how to optimize it?

The first way is to modify the code to change the loop of the for int into a for long type:

For (long item0; I

Welcome to subscribe "Shulou Technology Information " to get latest news, interesting things and hot topics in the IT industry, and controls the hottest and latest Internet news, technology news and IT industry trends.

Views: 0

*The comments in the above article only represent the author's personal views and do not represent the views and positions of this website. If you have more insights, please feel free to contribute and share.

Share To

Development

Wechat

© 2024 shulou.com SLNews company. All rights reserved.

12
Report