面試官:你回去等通知吧

面試官:你回去等通知吧

老規矩,先聊聊生活,上面這張圖片是我週一拍的。

週一晚上下班後發現公司樓下推著三輪車賣花的阿姨又開始買花了。整個路口只有她一個人在做生意,整條路上也沒有幾個人,大家都低著頭匆匆走著,繁花中帶著點憂傷。

於是,我去買了一把白玫瑰。

上週日把《霍亂時期的愛情》看完了,就剛好當道具拍了上面的照片。總體來說我不喜歡這種縱情聲色的故事,更不喜歡那個看起來冠冕堂皇的理由∶“我一生有622個情人,但是我只愛過你”。雖然它真的是窮極了愛情的所有可能性,但是它不夠真實。

相比之下我覺得錢鍾書先生寫的《圍城》∶“我說的讓她三分,不是三分流水七分塵的三分,而是天下明月只有三分的三分。”這樣打打鬧鬧的愛情更加真實。

再看楊絳先生的《我們仨》,書的最後她說∶“世間好物不堅牢,彩雲易散琉璃脆”。這才是愛情,這才是真實的生活。

好了,說迴文章。

對不起,我錯了。

前面發的這兩篇文章:

裡面有一些沒有說清楚的地方,又有很多讀者來問,所以我覺得需要補充說明一下

更重要的是,經過高手指點,其中還有一些描述錯誤的地方,我也需要進行勘誤

面試官:你回去等通知吧

如果真的是面試題,可能面試官就會對我說:好了,我們今天就先到這裡。你回去等通知吧。

面試官:你回去等通知吧

如果你沒看過我剛剛說的兩篇文章,我建議你不要看這篇,因為一看就得看三篇,如果裡面的衍生知識點你還想徹底弄明白,一個下午就過去了......(當然,你看了後收穫肯定還是有的。)

如果你看了我之前的兩篇文章,我求求你一定看看這篇,補充、更正一下答案,等面試官真的問起細節來,也不怕......

面試官:你回去等通知吧

好了,在閱讀本文之前,我假設你已經讀過我前面說的兩篇優質、幽默、有料的文章了。

併發的可達性分析-勘誤

之前發佈了這篇文章《面試官:你說你熟悉jvm?那你講一下併發的可達性分析》,對於文中這一部分內容中的動圖,有很多朋友給我說看不懂:

面試官:你回去等通知吧

我把這個動圖拿出來:

面試官:你回去等通知吧

首先,需要說明的是,我現在也看不懂這個動圖了。(畫錯了就是畫錯了,還強行找個理由)。

接下來,忘記這個動圖,我們重新分析一波原始快照方案(以下簡稱SATB,Snapshot At The Beginning)。

面試官:你回去等通知吧

首先,我們看初始標記階段(即根節點枚舉)完成後,剛剛進入併發標記階段,GC 線程開始掃描時的對象圖:

面試官:你回去等通知吧

在上面這張圖裡,當GC Roots確定後,對象圖就已經確定了。SATB掃描的時候基於已經確定的對象圖(快照版的對象圖)掃描,也就是說掃描過程中上面的快照圖的引用關係是不會發生變化的,但是真實的對象圖是會發生變化的。

舉個例子:就類似於你在操場上拍了一張照片,你數照片裡面的人數,照片是不會發生變化,人數一直都是這麼多,但是真實的操場上的人是在時刻變化的。

所以,在對象圖確定的一刻,正常掃描完成後,對象圖變成了下面這樣:

面試官:你回去等通知吧

好了,面前的鋪墊完成了。

我們這裡需要演示的是“對象消失”情況。

首先,我們先確定一下上面展示的對象圖,在併發標記階段必然有一個時刻的對象圖是這樣的:

面試官:你回去等通知吧

我們基於這個時刻的這個對象圖去討論“對象消失”的問題。

還得記得"對象消失"必須同時滿足的兩個條件嗎?(這兩個條件是摘抄自《深入理解Java虛擬機(第3版)》P.89)

條件一:賦值器插入了一條或者多條從黑色對象到白色對象的新引用。

條件二:賦值器刪除了全部從灰色對象到該白色對象的直接或間接引用。

我們再仔細的讀一遍第二個條件,你會發現,它說的是**“該白色對象”。這個“該白色對象”指的是條件一里面的白色對象。**

所以,我們有理由相信:條件一和條件二是有先後順序的,即必須是賦值器插入了一條或者多條從黑色對象到白色對象的新引用,然後賦值器又刪除了全部從灰色對象到該白色對象的直接或間接引用。在這樣的情況下,才會出現“對象消失”的情況。

經過高人指點,我們還可以進行反證法,如下:

我們假設灰色對象到白色對象的引用先刪除了,即先觸發了條件二。那麼對應的這個時刻真實的對象圖將變成下面的樣子:

(注意我這裡強調的是真實的對象圖,而不是快照的對象圖。再次重申:快照的對象圖在掃描開始的時候就確定了,掃描過程中是不會變化的。)

面試官:你回去等通知吧

那麼,白色對象9是處於遊離態的,從根節點沒有任何引用鏈相連,用圖論的話來說就是從 GC Root 到對象9不可達,則證明此對象是不可能再被使用的。因此用戶線程不可能把黑色對象5指向遊離態的白色對象9,你寫不出這樣的代碼來。

如果說上面的圖你一眼沒看出來,那麼請看下面這圖,是不是恍然大悟:

面試官:你回去等通知吧

黑色對象5不能指向白色對象9,那麼第一條規則就滿足不了了。

所以,綜上我們可以得出:條件一和條件二是有先後順序的。

那麼我們根據條件一繼續做圖如下:

面試官:你回去等通知吧

條件一是賦值器插入了一條或者多條從黑色對象到白色對象的新引用。

在上面這個圖的場景中,就是 GC 線程在工作的同時,賦值器插入了一條黑色對象5到白色對象9之間的新引用。(用紅色線條以示區分)

在這個時刻,由於灰色對象6指向白色對象9,所以黑色對象5可以指向白色對象9,想一想我們前面的證明,只要有引用鏈,黑色對象就可以到達白色對象。

這個時候僅僅滿足了條件一,對象還沒消失。

接下來就是條件二的圖,STAB破壞的就是條件二

面試官:你回去等通知吧

條件二是賦值器刪除了全部從灰色對象到該白色對象的直接或間接引用

在上面這個圖的場景中,就是賦值器刪除了灰色對象6到白色對象9的直接引用。

這個時候白色對象9就是“消失的對象”了,因為黑色的對象5是不會被再次掃描的。

需要注意的是,賦值器可以理解為用戶線程,由於在併發標記階段,用戶線程和 GC 線程在同時運行,所以需要出現上面的圖,還有一個前置條件就是:

用戶線程刪除對象6到對象9之間的引用,要先於 GC 線程掃描到對象6,把對象6變成灰色的操作。因為只有這樣,GC 線程處理到對象6的時候,才有對應的寫屏障記錄。

如果在 GC 線程已經掃描過對象6,即對象6已經是黑色的情況下(這個時候對象9,不是黑色就是灰色,不可能是白色),用戶線程再去刪除對象6到對象9之間的引用,GC 線程是不需要處理的,因為對象9已經是非白了,它在本輪中必定會活下來。

這裡我引用R大的描述:

https://hllvm-group.iteye.com/group/topic/44381?page=2

因為刪除操作會觸發 pre-write barrier,把每次引用關係變化時舊的引用值記下來,只有這樣,等 GC 線程到達某一個對象時,這個對象的所有引用類型字段的變化全都有記錄在案,就不會漏掉任何在快照圖裡活的對象。當然,很可能有對象在快照中是活的,但隨著併發 GC 的進行它可能本來已經死了,但 SATB 還是會讓它活過這次 GC,變成了浮動垃圾。

SATB 在寫屏障裡,把舊的引用所指向的對象都變成非白的(已經黑灰就不用管,還是白的就變成灰的)。

這樣做的實際效果是:如果一個灰對象的字段原本指向一個白對象,但在concurrent marker能掃描到這個字段之前,這個字段被賦上了別的值(例如說null),那麼這個字段跟白對象之間的關聯就被切斷了。SATB write barrier保證在這種切斷髮生之前就把字段原本引用的對象變灰,從而杜絕了上述條件二的發生。

其中:“把舊的引用所指向的對象都變成非白的。”在我們這個場景下含義如下:

舊的引用指的是:灰色對象6到白色對象9之間的引用。

所指向的對象指的是:白色對象9。

都變成非白的:指的是白色對象9變成了灰色。

所以,在兩個條件順序觸發、對象圖掃描完成後會變成下面的樣子:

面試官:你回去等通知吧

併發掃描結束之後,再以灰色對象9為根(把它作為根,自然會變成黑色),重新掃描一次,所以最終的對象圖變成了這樣:

面試官:你回去等通知吧

有的小夥伴就會問了:如果在標記過程中,用戶線程並沒有把對象5指向對象9的操作,僅僅是發生了刪除對象6到對象9之間引用的操作,那麼這個對象圖是什麼樣子呢?

就是下面這個樣子,你應該可以想象出來:

面試官:你回去等通知吧

對象9還是黑色,只是它變成了浮動垃圾,逃過了本次回收而已。並不影響程序運行。

接下來,讓上面的圖動起來,並且我把圖片之間的切換順序放慢。你再自己細品品:

面試官:你回去等通知吧

所以,上面的全部描述,才是一次我認為正確的,展示SATB方案是如何解決“對象消失”問題的過程。

之前《面試官:你說你熟悉jvm?那你講一下併發的可達性分析》中對於這一部分的描述過於簡單,且存在錯誤,給大家道歉,並特以此文進行修正。

你是什麼時候的垃圾-勘誤

在《G1回收器:我怎麼知道你是什麼時候的垃圾?》這篇文章中有一句描述是這樣的:

面試官:你回去等通知吧

“GC Roots 能直接關聯到的對象:就是一個 Region 已經使用過的部分,所以在 bottom 與 top 之間。”這句話是錯誤的。

實際上,通過文章後面的描述你也能發現。GC Roots 能直接關聯到的對象集合應該“小於” Region 已經使用過的部分,對象圖遞歸完之後,所有對象總和,才等於Region已經使用過的部分。

通過文章中後半部分的這個圖片也可以直觀的發現, bottom 到 top 之間是一個 Region 已經使用的部分。但是這一部分中,只有 bottom 到 NextTAMS 之間的對象才是 GC Roots 能直接關聯到的對象,這部分對象並不是一個 Region 已經使用過的部分。

面試官:你回去等通知吧

你是什麼時候的垃圾-補充說明

關於《G1回收器:我怎麼知道你是什麼時候的垃圾?》這篇文章,還有兩個需要補充說明的地方。

有的讀者問說:文章中沒有討論回收的內容,每次清理不會真正回收,那是不是多輪標記後才發生一次回收呢?

一。

首先,文章中確實沒有討論回收相關的內容。我在前面部分也寫了,把G1回收切分為兩大部分:

1.Global Concurrent Marking:全局併發標記。

2.Evacuation Pauses:該階段是負責把一部分Region裡的活對象拷貝到空Region裡面去,然後回收原本的Region空間。

只要清楚了全局併發標記階段,就可以解答文中拋出的這個問題:

面試官:你回去等通知吧

所以我只說明了全局併發標記階段。

如果想要了解回收階段的事,可以去看看R大的回答,強烈建議你看完本文,點個贊後,打開下面的鏈接,反覆閱讀幾遍:

https://hllvm-group.iteye.com/group/topic/44381

其次,“每次清理不會真正回收,那是不是多輪標記後才發生一次回收呢?”

這句話,可能是我在文章強調了清理階段不拷貝任何對象,再加上沒有描述回收階段,導致讀者有點懵了吧。

一次全局併發標記完成後,緊接著一次回收的過程。

只是G1收集器之所以能建立可預測的停頓時間模型(-XX:MaxGCPauseMillis指定,默認值為200毫秒),是因為它將 Region 作為單次回收的最小單元,即每次收集到的內存空間都是 Region 大小的整數倍,這樣就可以有計劃地避免在整個Java堆中進行全區域的垃圾回收。

更具體一點的做法就是每個 Region 裡面堆積的垃圾都有一個“價值”(價值即回收所獲得的空間大小以及回收所需要的時間的經驗值)。而這些“價值”,是維護在一個優先級列表中的,G1收集器都是知道的。

所以回收階段會優先處理回收價值最大的那些 Region。因此,一次回收的過程並不會回收所有的 Region。

二。

這裡也就解釋了讀者提出的另外一個問題:如果每次標記完都會回收整理,那為什麼紅框所在的區間與上一次標記之後相同,好像沒有被整理一樣,整理之後不是應該不留下內存空隙嗎?

面試官:你回去等通知吧

我覺得一個合理的解釋,就是我上面說的:這個 Region 的價值不夠,所以它本次沒有被回收。隨著時間的推移,它裡面堆積的垃圾越來越多,“價值”就越來越高,總是會被回收的。

還有讀者問:看了併發標記的過程,有個疑問 prevBitmap 的作用是什麼? 因為感覺每次都是從頭開始掃描,沒看到它的作用。

這個問題,可以從這張圖片入手解答:

面試官:你回去等通知吧

這個 E 是 Remark 階段,可以看到,在這個階段,其實 PrevBitmap 是派上用場了。

前面剛剛說了,這個 Region 由於“價值”不夠,它逃過了上次垃圾回收,所以待到下次垃圾回收的時候,就是 prevBitmap 的用武之地了,它裡面記錄的地址對應的區間就不需要再次標記了,因為這些地址對應的對象就已經是垃圾了。

我們可以假設 E 代表的是第 n 輪迴收的過程的Remark階段。那麼 PrevBitmap 就是第 n-1 輪的標記結果。

之前的文章說了:一個 previous Bitmap 記錄的是上一輪 Concurrent Marking 後的對象標記狀態,因為上一輪已經完成(上一輪就是第n-1輪),所以這個bitmap的信息可以直接使用。

可以直接使用的意思就是前面說的:它裡面記錄的地址對應的區間就不需要再次標記了,因為這些地址對應的對象就已經是垃圾了。

到 F 圖裡面,可以看到,當前的 F 圖是清理階段已經完成的狀態了:

面試官:你回去等通知吧

判斷標準有二:

1.和 E 圖相比PrevBitmap 和 NextBitmap 已經交換了位置。

2.PrevBitmap 裡面對應的地址的空間已經被標記為淺灰色了。

這個時候已經完成標記,PrevBitmap 又變成了第n-1次標記的結果。

你是什麼垃圾-懟人

因為之前的文章已經發布了,所以我需要修改一下對應的內容。提醒後面的讀者,如果看到了文章,需要注意這些地方描述的有問題。

但是我在查找我文章的過程中發現了一些讓我很鬱悶的事情,之前的文章,大都被剽竊了,我也見怪不怪,有時間就順手舉報一下了。

最過分的是下面這個:

面試官:你回去等通知吧

這是一個百家號賬號,一字不差的抄我文章,還自己標註為“原創”?

我去寫了個評論:

面試官:你回去等通知吧

他還不敢把評論放出來。

面試官:你回去等通知吧

還有下面這個,你可長點心吧。你配的這張圖片,我倒是想在家拍,但是我拍不出來呀:

面試官:你回去等通知吧

這樣的情況還有很多。說到底,就還是版權意識的問題。

版權問題,我之前在《訂閱號做了77天,我掙了487.52元》這篇文章裡面聊過:

面試官:你回去等通知吧

我的號不會傳播任何盜版資源,以前如此,現在如此,以後也會如此。

不做惡,就是最大的善。與君共勉。

所以我在此鄭重聲明,如果未經許可轉載我的文章,必須標明原文地址,且保留文末公眾號二維碼,否則我一定見一個舉報一個。

面試官:你回去等通知吧

我先舉報你涉黃,引起工作人員的注意,再舉報你抄襲,讓工作人員懲罰你。

氣死我了。

面試官:你回去等通知吧

最後說一句(求關注)

通過這件事我也再次感覺到了,看網上的野生文章(比如我的),要持有謹慎、懷疑、學習的態度。

面試官:你回去等通知吧

才疏學淺,難免會有紕漏,如果你發現了錯誤的地方,還請你留言給我指出來,我對其加以修改。(我每篇技術文章都有這句話,我是認真的說的。)

我是why技術,一個不是大佬,但是喜歡分享,又暖又有料的四川好男人。

以上。


分享到:


相關文章: