我就奇了怪了,STW到底是怎麼做到的

語言: CN / TW / HK

哈嘍,大家好,我是江湖人送外號[道格牙]的子牙老師。

今天想跟大家聊聊STW,它是怎麼給程式世界按下暫停鍵的

問題分析

STW,即Stop The World。

為什麼需要STW呢?試想你媽給你打掃房間的場景:把你攆出去,關上門,打掃乾淨,開啟門,數落你,揍你…一套標準化流程後,房間乾淨了。打完你,***心情變好了,打麻將都能多贏點。

這裡面有個關鍵環節:把你攆出去。儘管在打掃房間的過程中你可能不會製造垃圾,但是你的存在就有這個風險,所以必須把你攆出去。這話不是我說的,是從***行為中揣摩出來的。^_^

試想,如果不把你攆出去,你媽打掃垃圾的同時,你又陸陸續續製造了垃圾,那這場打掃房間的行動是不是變成了無法結束的行動啊。或者到某個時間點,你媽打掃了一半走了,丟下一句話:朽木不可雕也,孺子不可教也。

垃圾收集器也是一樣的,為了保證清理垃圾的完整性,在某些環節,就會STW。比如所有垃圾收集器中都有的一個階段:初始階段,即掃描根物件,需要STW。小夥伴門看過的幾乎所有資料,講到這基本就沒了對吧。但這不是子牙老師我的風格,咱們接著往後面說。

STW

JVM中要做到STW是很難的。為什麼這麼說呢?因為需要考慮很多很多因素。

一、JVM中存在多種型別的會發生改變記憶體行為的執行緒:

  1. 執行業務邏輯的使用者執行緒

  2. 執行native方法的Java執行緒

  3. 執行垃圾收集的GC執行緒

  4. 執行即時編譯的JIT執行緒

二、每種型別的執行緒個數,在需要STW的那一刻,可能都不止一個。

三、每種型別的執行緒,在需要STW的那一刻,執行到的程式碼位置也未可知。

四、每種型別的執行緒阻塞的點還不能隨機。因為執行緒在阻塞前需要更新OopMap。

OopMap是什麼?你可以理解成是記錄這個執行緒一路跑下來經歷過的所有Java物件的集合。為什麼要有OopMap呢?因為沒有的話,你就得掃描整個棧,去查詢根物件。

這裡說的只是查詢根物件的一種情況哈,勿擡杆,我會記仇。^_^

如何暫停執行緒

聽我這麼一分析,好像確實很複雜哈。那如果是你來實現,你會怎麼解決呢?小夥伴門可以想一想。經常想這樣有深度的問題,有利於提高你的思考深度。

我們還是來看看JVM是如何高明地解決的吧。

如果執行緒隨便哪個位置阻塞都合適,這個問題就會簡單一百倍。但是這裡簡單了,給其他地方就帶來了災難。就是說執行緒阻塞前需要更新OopMap,如果不更新,沒有這個資料的話,GC時就需要掃描所有執行緒的所有棧的所有棧幀來查詢根物件。

OopMap的存在,其實又是一種空間換時間的策略。因為相比記憶體的價格,降低GC延時明顯更重要。

但是JVM的執行流那麼多,何時?在什麼地方?更新OopMap呢?這就是安全點存在的意義。安全點同時解決了STW及更新OopMap。

其實也可以這樣說,不理解安全點就無法理解STW,甚至於無法理解GC。

安全點

安全點涉及的知識點非常多、非常底層。本篇文章就講安全點中與STW相關的知識點。其他的知識點後面會寫系列文章展開講。感興趣的小夥伴可以關注我公眾號關注我的發文動態:硬核子牙。

這段程式碼是大家看GC原始碼時經常看到的

SafepointSynchronize::begin

我把hotspot原始碼中 核心的程式碼 粘過來

這段程式碼到底做了哪些事情呢:

  1. 告訴JVM馬上要開始GC(下雨)了,開始做準備工作了(準備收衣服了)。本質就是修改一些屬性位。比如第5行程式碼,通知直譯器做好準備工作,迎接GC到來。

  2. 將polling_page對應的物理頁設定成不可讀狀態。 這步非常非常重要 。等下說。

  3. 不停檢測,確定是否所有的執行緒都已進入安全點。只有都已進入安全點,才能執行GC邏輯。

STW的真面目

安全點是如何解決讓所有的執行緒都阻塞的呢?開啟安全點為什麼要將物理頁的屬性改為不可讀呢?

因為JVM在生成執行流程式碼的時候,都會在適合作為安全點的地方插入一段程式碼

這段程式碼就是安全點的本質,也是觸發STW的本質。什麼意思呢?如果os::_polling_page對應的物理頁屬性是可讀的,這段程式碼並沒什麼特殊意義。但是如果是不可讀的,讀的時候就會觸發段異常,對應的作業系統訊號:SIGSEGV。

JVM捕獲了這個異常,並進行了處理。所有的執行緒都是在這個地方STW的。

這就是安全點難的地方,涉及到的知識點太多太底層!其實我搞手寫JVM小班的核心目的不是帶你寫一個JVM,其一是讓你通過手寫JVM瞭解hotspot的體系,你才能看得懂hotspot原始碼。其二,也是最核心的,掌握底層。因為掌握了底層,你對技術就沒有恐懼之心了,你會覺得你無所不能。事實上,相對的無所不能是可以做到的,只是需要時間沉澱。囉嗦了兩句哈。

GC結束後喚醒所有阻塞的執行緒,小夥伴們應該能想到是在哪裡?如何喚醒的了吧

推薦閱讀

1、搭建JVM框架,輸出hello,world

2、超快速定位OOM一攬子計劃

3、JVM的多型是如何實現的

4、從hotspot原始碼層面剖析Java的多型實現原理

你好,我是子牙。十餘年技術生涯,一路披荊斬棘從小白到技術總監到大廠中介軟體到創業。技術棧如彙編、C語言、C++、Windows核心、Linux核心及特別喜歡研究虛擬機器底層實現,對JVM有深入研究。分享的文章偏硬核,很硬的那種。不考慮交個朋友嗎?關注硬核子牙: