1. 
          

          1. 新聞動(dòng)態(tài)

            Stop The World 是何時(shí)發(fā)生的?

            常見(jiàn)問(wèn)題 發(fā)布者:ou3377 2021-12-11 08:55 訪(fǎng)問(wèn)量:95

            垃圾回收流程的一些流程

            哪些對象是垃圾?

            當我們進(jìn)行垃圾回收的時(shí)候,首先需要判斷哪些對象是存活的?

            常用的方法有如下兩種

            1. 引用計數法
            2. 可達性分析法

            Python判斷對象存活的算法用的是引用計數法,而Java則使用的是可達性分析法。

            「通過(guò)GC ROOT可達的對象,不能被回收,不可達的對象則可以被回收,搜索走過(guò)的路徑叫做引用鏈」

            不可達對象會(huì )進(jìn)行2次標記的過(guò)程,通過(guò)GC ROOT不可達,會(huì )被第一次標記。如果需要執行finalize()方法,則這個(gè)對象會(huì )被放入一個(gè)隊列中執行finalize(),如果在finalize()方法中成功和引用鏈上的其他對象關(guān)聯(lián),則會(huì )被移除可回收對象集合(「一般你不建議你使用finalize方法」),否則被回收

            「常見(jiàn)的GC ROOT有如下幾種」

            1. 虛擬機棧(棧幀中的本地變量表)中引用的對象
            2. 方法區中類(lèi)靜態(tài)屬性引用的對象
            3. 方法區中常量引用的對象
            4. 本地方法棧中JNI(Native方法)引用的對象

            「照這樣看,程序中的GC ROOT有很多,每次垃圾回收都要對GC ROOT的引用鏈分析一遍,感覺(jué)耗費的時(shí)間很長(cháng)啊,有沒(méi)有可能減少每次掃描的GC ROOT?」

            分代和跨代引用

            其實(shí)當前虛擬機大多數都遵循了“分代收集”理論進(jìn)行設計,它的實(shí)現基于2個(gè)分代假說(shuō)之上

            1. 絕大多數對象都是朝生夕滅的
            2. 熬過(guò)多次垃圾收集過(guò)程的對象就越難以消亡

            因此堆一般被分為新生代和老年代,針對新生代的GC叫MinorGC,針對老年代的GC叫OldGC。但是分代后有一個(gè)問(wèn)題,為了找到新生代的存活對象,不得不遍歷老年代,反過(guò)來(lái)也一樣圖片當進(jìn)行MinorGC的時(shí)候,如果我們只遍歷新生代,那么可以判定ABCD為存活對象。但是E不會(huì )被判斷為存活對象,所以就會(huì )有問(wèn)題。

            為了解決這種跨代引用的對象,最笨的辦法就是遍歷老年代的對象,找出這些跨代引用的對象。但這種方式對性能影響較大

            這時(shí)就不得不提到第三個(gè)假說(shuō)

            「跨代引用相對于同代引用來(lái)說(shuō)僅占極少數?!?/strong>

            根據這條假說(shuō),我們就不需要為了少量的跨代引用去掃描整個(gè)老年代。「為了避免遍歷老年代的性能開(kāi)銷(xiāo),垃圾回收器會(huì )引入一種記憶集的技術(shù),記憶集就是用來(lái)記錄跨代引用的表」

            如新生代的記憶集就保存了老年代持有新生代的引用關(guān)系

            所以在進(jìn)行MinorGC的時(shí)候,只需要將包含跨代引用的內存區域加入GC ROOT一起掃描就行了

            卡表

            前面我們說(shuō)到垃圾收集器用記憶集來(lái)記錄跨代引用。其實(shí)你可以把記憶集理解為接口,卡表理解為實(shí)現,類(lèi)比Map和HashMap。

            卡表最簡(jiǎn)單的形式可以只是一個(gè)字節數組, 而HotSpot虛擬機確實(shí)也是這樣做的。以下這行代碼是HotSpot默認的卡表標記邏輯:

            CARD_TABLE [this address >> 9] = 0;

            圖片HotSpot用一個(gè)數組元素來(lái)保存對應的內存地址是有有跨代引用對象(從this address右移9位可以看出每個(gè)元素映射了512字節的內存)

            當數組元素值為0時(shí)表明對應的內存地址不存在跨代引用對象,否則存在(稱(chēng)為卡表中這個(gè)元素變臟)

            如何更新卡表?

            「將卡表元素變臟的過(guò)程,HotSpot是通過(guò)寫(xiě)屏障來(lái)實(shí)現的」,即當其他代對象引用當前分代對象的時(shí)候,在引用賦值階段更新卡表,具體實(shí)現方式類(lèi)似于A(yíng)OP

            void oop_field_store(oop* field, oop new_value) 
            // 引用字段賦值操作
            *field = new_value;
            // 寫(xiě)后屏障,在這里完成卡表狀態(tài)更新 
            post_write_barrier(field, new_value);
            }

            三色標記法

            執行思路

            「如何判斷一個(gè)對象可達呢?這就不得不提到三色標記法」

            白色:剛開(kāi)始遍歷的時(shí)候所有對象都是白色的 灰色:被垃圾回收器訪(fǎng)問(wèn)過(guò),但至少還有一個(gè)引用未被訪(fǎng)問(wèn) 黑色:被垃圾回收器訪(fǎng)問(wèn)過(guò),并且這個(gè)對象的所有引用都被訪(fǎng)問(wèn)過(guò),是安全存活的對象(GC ROOT會(huì )被標記為黑色)

            圖片以上圖為例,三色標記法的執行流程如下

            1. 先將GC ROOT引用的對象B和E標記為灰色
            2. 接著(zhù)將B和E引用的對象A,C和F標記為灰色,此時(shí)B和E標記為黑色
            3. 依次類(lèi)推,最終被標記為白色的對象需要被回收

            三色標記法問(wèn)題

            可達性分析算法根節點(diǎn)枚舉這一步必須要在一個(gè)能保障一致性的快照中分析,所以要暫停用戶(hù)線(xiàn)程(Stop The World ,STW),在各種優(yōu)化技巧的加持下,停頓時(shí)間已經(jīng)非常短了。

            在從根節點(diǎn)掃描的過(guò)程則不需要STW,但是也會(huì )發(fā)生一些問(wèn)題。由于此時(shí)垃圾回收線(xiàn)程和用戶(hù)線(xiàn)程一直運行,所以引用關(guān)系會(huì )發(fā)生變化

            1. 應該被回收的對象被標記為不被回收
            2. 不應該被回收的對象標記為應該回收

            第一種情況影響不大,大不了后續回收即可。但是第二種情況則會(huì )造成致命錯誤

            所以經(jīng)過(guò)研究表明,只有同時(shí)滿(mǎn)足兩個(gè)條件才會(huì )發(fā)生第二種情況

            1. 插入了一條或者多條黑色到白色對象的引用
            2. 刪除了全部從灰色到白色對象的引用

            為了解決這個(gè)問(wèn)題,我們破壞2個(gè)條件中任意一個(gè)不就行了,由此產(chǎn)生了2中解決方案,「增量更新」「原始快照」。CMS使用的是增量更新,G1使用的是原始快照

            「增量更新要破壞的是第一個(gè)條件」, 當黑色對象插入新的指向白色對象的引用關(guān)系時(shí), 就將這個(gè)新插入的引用記錄下來(lái), 等并發(fā)掃描結束之后, 再將這些記錄過(guò)的引用關(guān)系中的黑色對象為根, 重新掃描一次。這可以簡(jiǎn)化理解為, 黑色對象一旦新插入了指向白色對象的引用之后, 它就變回灰色對象了

            「原始快照要破壞的是第二個(gè)條件」, 當灰色對象要刪除指向白色對象的引用關(guān)系時(shí), 就將這個(gè)要刪除的引用記錄下來(lái), 在并發(fā)掃描結束之后, 再將這些記錄過(guò)的引用關(guān)系中的灰色對象為根, 重新掃描一次。這也可以簡(jiǎn)化理解為, 無(wú)論引用關(guān)系刪除與否, 都會(huì )按照剛剛開(kāi)始掃描那一刻的對象圖快照來(lái)進(jìn)行搜索。

            參考自《深入理解Java虛擬機》

            垃圾收集器

            圖片圖中展示了七種作用于不同分代的收集器,如果兩個(gè)收集器之間存在連線(xiàn),就說(shuō)明它們可以搭配使用。在JDK8時(shí)將Serial+CMS,ParNew+Serial Old這兩個(gè)組合聲明為廢棄,并在JDK9中完全取消了這些組合的支持

            并行和并發(fā)都是并發(fā)編程中的專(zhuān)業(yè)名詞,在談?wù)摾占鞯纳舷挛恼Z(yǔ)境中, 它們可以理解為

            「并行(Parallel)」:指多條垃圾收集線(xiàn)程并行工作,但此時(shí)用戶(hù)線(xiàn)程仍然處于等待狀態(tài)

            「并發(fā)(Concurrent」):指用戶(hù)線(xiàn)程與垃圾收集線(xiàn)程同時(shí)執行

            Serial收集器

            「新生代,標記-復制算法,單線(xiàn)程。進(jìn)行垃圾收集時(shí),必須暫停其他所有工作線(xiàn)程,直到它收集結束」圖片

            ParNew收集器

            「ParNew本質(zhì)上是Serial收集器的多線(xiàn)程并行版本」圖片

            Parallel Scavenge收集器

            「新生代,標記復制算法,多線(xiàn)程,主要關(guān)注吞吐量」

            吞吐量=運行用戶(hù)代碼時(shí)間/(運行用戶(hù)代碼時(shí)間+運行垃圾收集時(shí)間)

            Serial Old收集器

            「老年代,標記-整理算法,單線(xiàn)程,是Serial收集器的老年代版本」

            用處有如下2個(gè)

            1. 在JDK5以及之前的版本中與Parallel Scavenge收集器搭配使用
            2. 作為CMS收集器發(fā)生失敗時(shí)的后備預案,在并發(fā)收集發(fā)生Concurrent Mode Failure時(shí)使用

            Parallel Old收集器

            「老年代,標記-整理算法,多線(xiàn)程,是Parallel Scavenge收集器的老年代版本」

            在注重吞吐量或者處理器資源較為稀缺的場(chǎng)合,都可以?xún)?yōu)先考慮Parallel Scavenge加Parallel Old收集器這個(gè)組合圖片

            CMS收集器

            「老年代,標記-清除算法,多線(xiàn)程,主要關(guān)注延遲」

            運作過(guò)程分為4個(gè)步驟

            1. 初始標記(CMS initial mark)
            2. 并發(fā)標記(CMS concurrent mark)
            3. 重新標記(CMS remark)
            4. 并發(fā)清除(CMS concurrent sweep)

            圖片

            1. 初始標記:標記一下GC Roots能直接關(guān)聯(lián)到的對象,速度很快(這一步會(huì )發(fā)生STW)
            2. 并發(fā)標記:從GC Roots的直接關(guān)聯(lián)對象開(kāi)始遍歷整個(gè)對象圖的過(guò)程,這個(gè)過(guò)程耗時(shí)較長(cháng)但是不需要停頓用戶(hù)線(xiàn)程,可以與垃圾收集一起并發(fā)運行
            3. 重新標記:為了修正并發(fā)標記期間,因用戶(hù)程序繼續運作而導致標記產(chǎn)生變動(dòng)的那一部分對象的標記記錄(「就是三色標記法中的增量更新」,這一步也會(huì )發(fā)生STW)
            4. 并發(fā)清除:清理刪除掉標記階段判斷的已經(jīng)死亡的對象,由于不需要移動(dòng)存活對象,所以看這個(gè)階段也是可以與用戶(hù)線(xiàn)程同時(shí)并發(fā)的

            總結

            收集器收集對象和算法收集器類(lèi)型說(shuō)明適用場(chǎng)景
            Serial新生代,復制算法單線(xiàn)程
            簡(jiǎn)單高效;適合內存不大的情況
            ParNew新生代,復制算法并行的多線(xiàn)程收集器ParNew垃圾收集器是Serial收集器的多線(xiàn)程版本搭配CMS垃圾回收器的首選
            Parallel Scavenge吞吐量?jì)?yōu)先收集器新生代,復制算法并行的多線(xiàn)程收集器類(lèi)似ParNew,更加關(guān)注吞吐量,達到一個(gè)可控制的吞吐量本身是Server級別多CPU機器上的默認GC方式,主要適合后臺運算不需要太多交互的任務(wù)


            收集器收集對象和算法收集器類(lèi)型說(shuō)明適用場(chǎng)景
            Serial Old老年代,標記整理算法單線(xiàn)程
            Client模式下虛擬機使用
            Parallel Old老年代,標記整理算法并行的多線(xiàn)程收集器Paraller Scavenge收集器的老年代版本,為了配置Parallel Svavenge的面向吞吐量的特性而開(kāi)發(fā)的對應組合在注重吞吐量以及CPU資源敏感的場(chǎng)合采用
            CMS老年代,標記清除算法并行與并發(fā)收集器盡可能的縮短垃圾收集時(shí)用戶(hù)線(xiàn)程停止時(shí)間;缺點(diǎn)在于,1.內存碎片,2.需要更多CPU資源,3.浮動(dòng)垃圾問(wèn)題,需要更大的堆空間重視服務(wù)的相應速度,系統停頓時(shí)間和用戶(hù)體驗的互聯(lián)網(wǎng)網(wǎng)站或者B/S系統?;ヂ?lián)網(wǎng)后端目前cms是主流的垃圾回收器
            G1跨新生代和老年代;標記整理+化整為零
            并行與并發(fā)收集器JDK1.7才正式引入,采用分區回收的思維,基本不犧牲吞吐量的前提下完成低停頓的內存回收;可預測的停頓是其最大的優(yōu)勢



            關(guān)鍵字: 分代和跨代引用 GC Roots

            文章連接: http://www.gostscript.com/cjwt/801.html

            版權聲明:文章由 晨展科技 整理收集,來(lái)源于互聯(lián)網(wǎng)或者用戶(hù)投稿,如有侵權,請聯(lián)系我們,我們會(huì )立即刪除。如轉載請保留

            双腿国产亚洲精品无码不卡|国产91精品无码麻豆|97久久久久久久极品|无码人妻少妇久久中文字幕
                1.