聊聊JVM(六)理解JVM的safepoint
safepoint是JVM里面很重要的一個概念,在很多場景下都會看到它,尤其是在GC的時候。這篇講講safepoint。本人不是做JVM實現(xiàn)研究的,很多地方只能點(diǎn)到為止,希望能夠講清楚這個概念,具體的細(xì)節(jié)可以自己去找資料深入研究。
?
safepoint 安全點(diǎn)顧名思義是指一些特定的位置,當(dāng)線程運(yùn)行到這些位置時,線程的一些狀態(tài)可以被確定(the thread's representation of it's Java machine state is well described),比如記錄OopMap的狀態(tài),從而確定GC Root的信息,使JVM可以安全的進(jìn)行一些操作,比如開始GC。
safepoint指的特定位置主要有:
1. 循環(huán)的末尾 (防止大循環(huán)的時候一直不進(jìn)入safepoint,而其他線程在等待它進(jìn)入safepoint)
2. 方法返回前
3. 調(diào)用方法的call之后
4. 拋出異常的位置
?
之所以選擇這些位置作為safepoint的插入點(diǎn),主要的考慮是“避免程序長時間運(yùn)行而不進(jìn)入safepoint”,比如GC的時候必須要等到Java線程都進(jìn)入到safepoint的時候VMThread才能開始執(zhí)行GC,如果程序長時間運(yùn)行而沒有進(jìn)入safepoint,那么GC也無法開始,JVM可能進(jìn)入到Freezen假死狀態(tài)。在stackoverflow上有人提到過一個問題,由于BigInteger的pow執(zhí)行時JVM沒有插入safepoint,導(dǎo)致大量運(yùn)算時線程一直無法進(jìn)入safepoint,而GC線程也在等待這個Java線程進(jìn)入safepoint才能開始GC,結(jié)果JVM就Freezen了。
How to get Java stacks when JVM can't reach a safepoint
?
JVM在很多場景下使用到safepoint, 最常見的場景就是GC的時候。對一個Java線程來說,它要么處在safepoint,要么不在safepoint。
1. Garbage collection pauses
2. Code deoptimization
3. Flushing code cache
4. Class redefinition (e.g. hot swap or instrumentation)
5. Biased lock revocation
6. Various debug operation (e.g. deadlock check or stacktrace dump)
?
GC的標(biāo)記階段需要stop the world,讓所有Java線程掛起,這樣JVM才可以安全地來標(biāo)記對象。safepoint可以用來實現(xiàn)讓所有Java線程掛起的需求。這是一種"主動式"(Voluntary Suspension)的實現(xiàn)。JVM有兩種執(zhí)行方式:解釋型和編譯型(JIT),JVM要保證這兩種執(zhí)行方式下safepoint都能工作。
在JIT執(zhí)行方式下,JIT編譯的時候直接把safepoint的檢查代碼加入了生成的本地代碼,當(dāng)JVM需要讓Java線程進(jìn)入safepoint的時候,只需要設(shè)置一個標(biāo)志位,讓Java線程運(yùn)行到safepoint的時候主動檢查這個標(biāo)志位,如果標(biāo)志被設(shè)置,那么線程停頓,如果沒有被設(shè)置,那么繼續(xù)執(zhí)行。
例如hotspot在x86中為輪詢safepoint會生成一條類似于“test %eax,0x160100”的指令,JVM需要進(jìn)入gc前,先把0x160100設(shè)置為不可讀,那所有線程執(zhí)行到檢查0x160100的test指令后都會停頓下來
?
?0x01b6d627: call 0x01b2b210 ; OopMap{[60]=Oop off=460}
;*invokeinterface size
; - Client1::main@113 (line 23)
; {virtual_call}
0x01b6d62c: nop ; OopMap{[60]=Oop off=461}
;*if_icmplt
; - Client1::main@118 (line 23)
0x01b6d62d: test %eax,0x160100 ; {poll}
0x01b6d633: mov 0x50(%esp),%esi
0x01b6d637: cmp %eax,%esi
在解釋器執(zhí)行方式下,JVM會設(shè)置一個2字節(jié)的dispatch tables,解釋器執(zhí)行的時候會經(jīng)常去檢查這個dispatch tables,當(dāng)有safepoint請求的時候,就會讓線程去進(jìn)行safepoint檢查。
?
?
聊聊JVM(五)從JVM角度理解線程?說了JVM中的線程類型,其中提到VMThread。VMThread會一直等待直到VMOperationQueue中有操作請求出現(xiàn),比如GC請求。而VMThread要開始工作必須要等到所有的Java線程進(jìn)入到safepoint。JVM維護(hù)了一個數(shù)據(jù)結(jié)構(gòu),記錄了所有的線程,所以它可以快速檢查所有線程的狀態(tài)。當(dāng)有GC請求時,所有進(jìn)入到safepoint的Java線程會在一個Thread_Lock鎖阻塞,直到當(dāng)JVM操作完成后,VM釋放Thread_Lock,阻塞的Java線程才能繼續(xù)運(yùn)行。
?
GC stop the world的時候,所有運(yùn)行Java code的線程被阻塞,如果運(yùn)行native code線程不去和Java代碼交互,那么這些線程不需要阻塞。VM操作相關(guān)的線程也不會被阻塞。
?
safepoint只能處理正在運(yùn)行的線程,它們可以主動運(yùn)行到safepoint。而一些Sleep或者被blocked的線程不能主動運(yùn)行到safepoint。這些線程也需要在GC的時候被標(biāo)記檢查,JVM引入了safe region的概念。safe region是指一塊區(qū)域,這塊區(qū)域中的引用都不會被修改,比如線程被阻塞了,那么它的線程堆棧中的引用是不會被修改的,JVM可以安全地進(jìn)行標(biāo)記。線程進(jìn)入到safe region的時候先標(biāo)識自己進(jìn)入了safe region,等它被喚醒準(zhǔn)備離開safe region的時候,先檢查能否離開,如果GC已經(jīng)完成,那么可以離開,否則就在safe region呆在。這可以理解,因為如果GC還沒完成,那么這些在safe region中的線程也是被stop the world所影響的線程的一部分,如果讓他們可以正常執(zhí)行了,可能會影響標(biāo)記的結(jié)果
?
可以設(shè)置JVM參數(shù)?-XX:+PrintSafepointStatistics –XX:PrintSafepointStatisticsCount=1?來輸出safepoint的統(tǒng)計信息
?
參考資料:??
找出棧上的指針/引用
What is a Java safepoint
GC safe-point (or safepoint) and safe-region
內(nèi)存篇:JVM內(nèi)存回收理論與實現(xiàn)
Safepoints in HotSpot JVM
總結(jié)
以上是生活随笔為你收集整理的聊聊JVM(六)理解JVM的safepoint的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 聊聊JVM(五)从JVM角度理解线程
- 下一篇: 聊聊JVM(八)说说GC标记阶段的一些事