JVM中如何做到STW使程序暫停
為什么需要STW呢?試想你媽給你打掃房間的場景:把你攆出去,關上門,打掃干凈,打開門,數(shù)落你,揍你…一套標準化流程后,房間干凈了。打完你,***心情變好了,打麻將都能多贏點。
這里面有個關鍵環(huán)節(jié):把你攆出去。盡管在打掃房間的過程中你可能不會制造垃圾,但是你的存在就有這個風險,所以必須把你攆出去。這話不是我說的,是從***行為中揣摩出來的。^_^
試想,如果不把你攆出去,你媽打掃垃圾的同時,你又陸陸續(xù)續(xù)制造了垃圾,那這場打掃房間的行動是不是變成了無法結束的行動啊。或者到某個時間點,你媽打掃了一半走了,丟下一句話:朽木不可雕也,孺子不可教也。
垃圾收集器也是一樣的,為了保證清理垃圾的完整性,在某些環(huán)節(jié),就會STW。比如所有垃圾收集器中都有的一個階段:初始階段,即掃描根對象,需要STW。小伙伴門看過的幾乎所有資料,講到這基本就沒了對吧。但這不是子牙老師我的風格,咱們接著往后面說。
STW
JVM中要做到STW是很難的。為什么這么說呢?因為需要考慮很多很多因素。
一、JVM中存在多種類型的會發(fā)生改變內存行為的線程:
執(zhí)行業(yè)務邏輯的用戶線程
執(zhí)行native方法的Java線程
執(zhí)行垃圾收集的GC線程
執(zhí)行即時編譯的JIT線程
二、每種類型的線程個數(shù),在需要STW的那一刻,可能都不止一個。
三、每種類型的線程,在需要STW的那一刻,執(zhí)行到的代碼位置也未可知。
四、每種類型的線程阻塞的點還不能隨機。因為線程在阻塞前需要更新OopMap。
OopMap是什么?你可以理解成是記錄這個線程一路跑下來經歷過的所有Java對象的集合。為什么要有OopMap呢?因為沒有的話,你就得掃描整個棧,去查找根對象。
這里說的只是查找根對象的一種情況哈,勿抬桿,我會記仇。^_^
如何暫停線程
聽我這么一分析,好像確實很復雜哈。那如果是你來實現(xiàn),你會怎么解決呢?小伙伴門可以想一想。經常想這樣有深度的問題,有利于提高你的思考深度。
我們還是來看看JVM是如何高明地解決的吧。
如果線程隨便哪個位置阻塞都合適,這個問題就會簡單一百倍。但是這里簡單了,給其他地方就帶來了災難。就是說線程阻塞前需要更新OopMap,如果不更新,沒有這個數(shù)據的話,GC時就需要掃描所有線程的所有棧的所有棧幀來查找根對象。
OopMap的存在,其實又是一種空間換時間的策略。因為相比內存的價格,降低GC延時明顯更重要。
但是JVM的執(zhí)行流那么多,何時?在什么地方?更新OopMap呢?這就是安全點存在的意義。安全點同時解決了STW及更新OopMap。
其實也可以這樣說,不理解安全點就無法理解STW,甚至于無法理解GC。
安全點
安全點涉及的知識點非常多、非常底層。本篇文章就講安全點中與STW相關的知識點。其他的知識點后面會寫系列文章展開講。感興趣的小伙伴可以關注我公眾號關注我的發(fā)文動態(tài):硬核子牙。
這段代碼是大家看GC源碼時經常看到的
SafepointSynchronize::begin
我把hotspot源碼中核心的代碼粘過來
這段代碼到底做了哪些事情呢:
告訴JVM馬上要開始GC(下雨)了,開始做準備工作了(準備收衣服了)。本質就是修改一些屬性位。比如第5行代碼,通知解釋器做好準備工作,迎接GC到來。
將polling_page對應的物理頁設置成不可讀狀態(tài)。這步非常非常重要。等下說。
不停檢測,確定是否所有的線程都已進入安全點。只有都已進入安全點,才能執(zhí)行GC邏輯。
STW的真面目
安全點是如何解決讓所有的線程都阻塞的呢?開啟安全點為什么要將物理頁的屬性改為不可讀呢?
因為JVM在生成執(zhí)行流代碼的時候,都會在適合作為安全點的地方插入一段代碼
這段代碼就是安全點的本質,也是觸發(fā)STW的本質。什么意思呢?如果os::_polling_page對應的物理頁屬性是可讀的,這段代碼并沒什么特殊意義。但是如果是不可讀的,讀的時候就會觸發(fā)段異常,對應的操作系統(tǒng)信號:SIGSEGV。
JVM捕獲了這個異常,并進行了處理。所有的線程都是在這個地方STW的。
這就是安全點難的地方,涉及到的知識點太多太底層!其實我搞手寫JVM小班的核心目的不是帶你寫一個JVM,其一是讓你通過手寫JVM了解hotspot的體系,你才能看得懂hotspot源碼。其二,也是最核心的,掌握底層。因為掌握了底層,你對技術就沒有恐懼之心了,你會覺得你無所不能。事實上,相對的無所不能是可以做到的,只是需要時間沉淀。啰嗦了兩句哈。
GC結束后喚醒所有阻塞的線程,小伙伴們應該能想到是在哪里?如何喚醒的了吧
以上就是JVM中如何做到STW使程序暫停的詳細內容,更多關于STW如何暫停程序的資料請關注腳本之家其它相關文章!
相關文章
Java并發(fā)編程之CountDownLatch的使用
CountDownLatch是一個倒數(shù)的同步器,常用來讓一個線程等待其他N個線程執(zhí)行完成再繼續(xù)向下執(zhí)行,本文主要介紹了CountDownLatch的具體使用方法,感興趣的可以了解一下2023-05-05SpringMVC返回的ResponseEntity出現(xiàn)亂碼及解決
這篇文章主要介紹了SpringMVC返回的ResponseEntity出現(xiàn)亂碼及解決方案,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2022-02-02IDEA教程創(chuàng)建SpringBoot前后端分離項目示例圖解
在使用spring、mybatis等框架時,配置文件很復雜,有時復雜的讓人想放棄Java,使用C#。springboot出現(xiàn)這一切問題就都不是問題2021-10-10