国产 无码 综合区,色欲AV无码国产永久播放,无码天堂亚洲国产AV,国产日韩欧美女同一区二区

從原理聊JVM(一):染色標記和垃圾回收算法

這篇具有很好參考價值的文章主要介紹了從原理聊JVM(一):染色標記和垃圾回收算法。希望對大家有所幫助。如果存在錯誤或未考慮完全的地方,請大家不吝賜教,您也可以點擊"舉報違法"按鈕提交疑問。

作者:京東科技?康志興

1 JVM運行時內存劃分

1.1 運行時數(shù)據(jù)區(qū)域

? 方法區(qū)

屬于共享內存區(qū)域,存儲已被虛擬機加載的類信息、常量、靜態(tài)變量、即時編譯器編譯后的代碼等數(shù)據(jù)。運行時常量池,屬于方法區(qū)的一部分,用于存放編譯期生成的各種字面量和符號引用。

JDK1.8之前,Hotspot虛擬機對方法區(qū)的實現(xiàn)叫做永久代,1.8之后改為元空間。二者區(qū)別主要在于永久代是在JVM虛擬機中分配內存,而元空間則是在本地內存中分配的。很多類是在運行期間加載的,它們所占用的空間完全不可控,所以改為使用本地內存,避免對JVM內存的影響。根據(jù)《Java虛擬機規(guī)范》的規(guī)定,如果方法區(qū)無法滿足新的內存分配需求時,將拋出OutOfMemoryError異常。

?

線程共享,主要是存放對象實例和數(shù)組。如果在Java堆中沒有內存完成實例分配,并且堆也無法再擴展時,Java虛擬機將會拋出OutOfMemoryError異常。PS:實際上寫入時并不完全共享,JVM會為線程在堆上劃分一塊專屬的分配緩沖區(qū)來提高對象分配效率。詳見:TLAB

? 虛擬機棧

線程私有,方法執(zhí)行的過程就是一個個棧幀從入棧到出棧的過程。每個方法在執(zhí)行時都會創(chuàng)建一個棧幀(Stack Frame)用于存儲局部變量表、操作數(shù)棧、動態(tài)鏈接、方法出口等信息。如果線程入棧的棧幀超過限制就會拋出StackOverFlowError,如果支持動態(tài)擴展,那么擴展時申請內存失敗則拋出OutOfMemoryError。

? 本地方法棧

和虛擬機棧的功能類似,區(qū)別是作用于Native方法。

? 程序計數(shù)器

線程私有,記錄著當前線程所執(zhí)行的字節(jié)碼的行號。其作用主要是多線程場景下,記錄線程中指令的執(zhí)行位置。以便被掛起的線程再次被激活時,CPU能從其掛起前執(zhí)行的位置繼續(xù)執(zhí)行。唯一一個在 Java 虛擬機規(guī)范中沒有規(guī)定任何 OutOfMemoryError 情況的區(qū)域。注意:如果線程執(zhí)行的是個java方法,那么計數(shù)器記錄虛擬機字節(jié)碼指令的地址。如果為native(底層方法),那么計數(shù)器為空。

1.2 對象的內存布局

在 HotSpot 虛擬機中,對象分為如下3塊區(qū)域:

? 對象頭(Header)運行時數(shù)據(jù):哈希碼、GC分代年齡、鎖狀態(tài)標志、偏向線程ID、偏向時間戳等。類型指針:對象的類型元數(shù)據(jù)的指針,如果對象是數(shù)據(jù),還會記錄數(shù)組長度。

? 對象實例數(shù)據(jù)(Instance Data)包含對象真正的內容,即其包括父類所有字段的值。

? 對齊填充(Padding)對象大小必須是是8字節(jié)的整數(shù)倍,所以對象大小不滿足這個條件時,需要用對齊填充來補齊。

2 標記的方法和流程

2.1 判斷對象是否需要被回收

要分辨一個對象是否可以被回收,有兩種方式:引用計數(shù)法可達性算法。

? 引用計數(shù)法就是在對象被引用時,計數(shù)加1,引用斷開時,計數(shù)減1。那么一個對象的引用計數(shù)為0時,說明這個對象可以被清除。這個算法的問題在于,如果A對象引用B的同時,B對象也引用A,即循環(huán)引用,那么雖然雙方的引用計數(shù)都不為0,但如果僅僅被對方引用實際上沒有存在的價值,應該被GC掉。

? 可達性算法通過引用計數(shù)法的缺陷可以看出,從被引用一方去判定其是否應該被清理過于片面,所以我們可以通過相反的方向去定位對象的存活價值:一個存活對象引用的所有對象都是不應該被清除的(Java中軟引用或弱引用在GC時有不同判定表現(xiàn),不在此深究)。這些查找起點被稱為GC Root。

2.2 哪些對象可以作為GC Root呢?

  1. JAVA虛擬機棧中的本地變量引用對象

  2. 方法區(qū)中靜態(tài)變量引用的對象

  3. 方法區(qū)中常量引用的對象

  4. 本地方法棧中JNI引用的對象

2.3 快速找到GC Root - OopMap

棧與寄存器都是無狀態(tài)的,保守式垃圾收集會直接線性掃描棧,再判斷每一串數(shù)字是不是引用,而HotSpot采用準確式垃圾收集方式,所有對象都存放在OopMap(Ordinary Object Pointer)中,當GC發(fā)生時,直接從這個map中尋找GC Root。

將GC Root存放到OopMap有兩個觸發(fā)時間點:

  1. 類加載完成后,HotSpot就會把對象內什么偏移量上是什么類型的數(shù)據(jù)計算出來。

  2. 即時編譯過程中,也會在特定的位置記錄下棧里和寄存器里哪些位置是引用。

2.4 更新OopMap的時機 - 安全點

導致OopMap更新的指令非常多,所以HotSpot只在特定位置進行記錄更新,這些位置叫做安全點。安全點位置的選取的標準是:“是否具有讓程序長時間執(zhí)行”。比如方法調用、循環(huán)跳轉、異常跳出等等。

2.5 可達性分析過程

三色標記法

? 白色:表示垃圾回收過程中,尚未被垃圾收集器訪問過的對象,在可達性分析開始階段,所有對象都是白色的,即不可達。

? 黑色:被垃圾收集器訪問過的對象,且這個對象所有的引用均掃描過。黑色的對象是安全存活的,如果其他對象被訪問時發(fā)現(xiàn)其引用了黑色對象,該黑色對象也不會再被掃描。

? 灰色:被垃圾收集器訪問過的對象,但這個對象至少有一個引用的對象沒有被掃描過。那么標記階段就是從GC Root的開始,沿著其引用鏈將每一個對象從白色標記為灰色最后標記為黑色的過程。

標記過程中不一致問題

由于這個階段是層層遞進的標記,所以過程中難免出現(xiàn)不一致的情況導致原本是黑色的對象被標記為白色,比如,當前掃描到B對象了,C對象尚未被訪問時,標記情況如下:

那么如果這時A對象取消了對B對象的引用,而GC Root增加了對C對象的引用,GC Root作為黑色標記不會再次被掃描,那么C對象在標記階段結束后仍然會保持白色,就會被清除掉。

解決方式

? 增量更新

當黑色對象增加了對白色對象的引用時,將其從黑色改為灰色,等并發(fā)標記階段結束后,從GC Root開始順著對象圖再將灰色對象重新掃描一次,這個掃描過程會STW,不會再次產(chǎn)生不一致問題。CMS就采用了這種方式。

? 原始快照(SATB)

當灰色對象刪除了白色對象的引用時,將其記錄在線程獨占的SATB Queue中,讓其在標記階段結束后被再次掃描。 G1、Shenandoah采用了這種方式。

示例

我們通過一個例子來展示兩種處理方式的不同,比如正常標記到對象A時,將其標記為灰色:

此時,用戶線程發(fā)生如下行為:

  1. GC Root直接引用了C

  2. A取消了引用B

理論上,C仍然是可達對象,不應被清除,而B不可達,應當被清除。

增量更新會記錄行為1,將GC Root標記為灰色,B不能訪問到被標記為可以回收

等到重新標記階段再次訪問灰色的GC Root,順序將GC Root和C標記為黑色:

而原始快照會記錄行為2,將發(fā)生引用變化的對象全部記錄下來,等到重新標記階段再次訪問這些灰色,將其標記為黑色并順著對象圖掃描。

那么最終B作為浮動垃圾就被保存下來了,只能等到下一次GC時才能被回收。

3 分代模型

3.1 分代假說

弱分代假說(WeakGenerationalHypothesis):絕大多數(shù)對象都是朝生夕滅的。 強分代假說(StrongGenerationalHypothesis):熬過越多次垃圾收集過程的對象就越難以消亡。 跨代引用假說(IntergenerationalReferenceHypothesis):跨代引用相對于同代引用來說僅占極少數(shù)。

上述假說是根據(jù)實際經(jīng)驗得來的,由此垃圾收集器通常分為“年輕代”和“年老代”:

? 年輕代用來存放不斷生成且生命周期短暫的對象,收集動作相對高頻

? 年老代用來存放經(jīng)歷多次GC仍然存活的對象,收集動作相對低頻

3.2 空間分配擔保

如果在GC后新生代存貨對象過多,Survivor無法容納,那么將會把這些對象直接送入年老代,這就叫年老代進行了“分配擔?!?。 為了保證年老代能夠足夠空間容納這些直接晉升的對象,在發(fā)生Minor GC之前,虛擬機必須先檢查年老代最大可用的連續(xù)空間,如果大于新生代所有對象總空間或者歷次晉升的平均大小,就會進行MinorGC,否則將進行FullGC以同時清理年老代。

3.3 記憶集和卡表

記憶集是一種用于記錄從非收集區(qū)域指向收集區(qū)域的指針集合的抽象數(shù)據(jù)結構。

記憶集的作用

新生代發(fā)生垃圾收集時(Minor GC),如果想確定這個新生代對象是否被年老代的對象引用,則需要掃描整個年老代,成本非常高。

如果我們能知道哪一部分年老代可能存在對新生代的引用,就可以降低掃描范圍。

所以我們可以在新生代建立一個全局數(shù)據(jù)結構叫“記憶集(Remembered Set)”,這個結構把年老代分為若干個小塊,標記了哪些小塊內存中存在引用了新生代對象的情況,等到Minor GC時,只掃描這部分存在跨代引用的內存塊即可。雖然在對象變化時增加了維護記憶集的成本,但相比垃圾收集時掃描整個年老代來說是值得的。

JVM通常在對象增加引用前設置寫屏障判斷是否發(fā)生跨代引用,如果有跨代情況,則更新記憶集。

卡表

實現(xiàn)記憶集時,可以有不同精度的粒度:可以指向內存地址,也可以指向某個對象,或者指向某一塊內存區(qū)域。精度越低,維護成本越低。指向某一塊內存區(qū)域的實現(xiàn)方式就是“卡表”??ū硗ǔ>褪且粋€byte數(shù)組,數(shù)組中每一個元素代表某一塊內存,其值是1或者0:當發(fā)生跨代引用時,就表示該元素“dirty”了,那么將將其設置為1,否則就是0。

4 垃圾回收算法

4.1 標記-清除(Mark-Sweep)

GC分為兩個階段,標記和清除。首先標記所有可回收的對象,在標記完成后統(tǒng)一回收所有被標記的對象。

缺點是清除后會產(chǎn)生不連續(xù)的內存碎片。碎片過多會導致以后程序運行時需要分配較大對象時,無法找到足夠的連續(xù)內存,而不得已再次觸發(fā)GC。

4.2 標記-復制(Mark-Copy)

將內存按容量劃分為兩塊,每次只使用其中一塊。當這一塊內存用完了,就將存活的對象復制到另一塊上,然后再把已使用的內存空間一次清理掉。

這樣使得每次都是對半個內存區(qū)回收,也不用考慮內存碎片問題,簡單高效。

缺點需要兩倍的內存空間。

一種優(yōu)化方式是使用eden和survivior區(qū),具體步驟如下:

eden和survivior區(qū)默認內存空間占比為8:1:1,同一時間只使用eden區(qū)和其中一個survivior區(qū)。標記完成后,將存活對象復制到另一個未使用的survivior區(qū)(部分年齡過大的對象將升級到年老代)。

這種做法,相比普通的兩塊空間的標記復制算法來說,只有10%的內存空間浪費,而這樣做的原因是:大部分情況下,一次young gc后剩余的存活對象非常少。

4.3 標記-整理(Mark-Compact)

標記-整理也分為兩個階段,首先標記可回收的對象,再將存活的對象都向一端移動,然后清理掉邊界以外的內存。

此方法避免標記-清除算法的碎片問題,同時也避免了復制算法的空間問題。 一般年輕代中執(zhí)行GC后,會有少量的對象存活,就會選用復制算法,只要付出少量的存活對象復制成本就可以完成收集。

而年老代中因為對象存活率高,用標記復制算法時數(shù)據(jù)復制效率較低,且空間浪費較大。所以需要使用標記-清除或者標記-整理算法來進行回收。

所以通??梢韵仁褂脴擞浨宄惴?,當碎片率高時,再使用標記整理算法。

5 最后

本篇介紹了JVM中垃圾回收器相關的基礎知識,后續(xù)會深入介紹CMS、G1、ZGC等不同垃圾收集器的運作流程和原理,歡迎關注。文章來源地址http://www.zghlxwxcb.cn/news/detail-420469.html

到了這里,關于從原理聊JVM(一):染色標記和垃圾回收算法的文章就介紹完了。如果您還想了解更多內容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關文章,希望大家以后多多支持TOY模板網(wǎng)!

本文來自互聯(lián)網(wǎng)用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。如若轉載,請注明出處: 如若內容造成侵權/違法違規(guī)/事實不符,請點擊違法舉報進行投訴反饋,一經(jīng)查實,立即刪除!

領支付寶紅包贊助服務器費用

相關文章

  • 垃圾回收 -標記清除算法

    垃圾回收 -標記清除算法

    就如他的字面意思一樣,由標記階段和清除階段構成。標記階段是把所有的活動對象都做上標記的階段。清除階段是把那些沒有標記的對象,也就是非活動對象回收的階段。通過這兩個階段,就可以令不能利用的內存空間重新得到利用。 在標記階段中,collector會為堆里所有活

    2024年02月10日
    瀏覽(28)
  • JVM回收算法(標記-清除算法, 復制算法, 標記-整理算法)

    JVM回收算法(標記-清除算法, 復制算法, 標記-整理算法)

    最基礎的算法,分為兩個階段,“標記”和“清除” 原理: - 標記階段:collector從mutator根對象開始進行遍歷,對從mutator根對象可以訪問到的對象都打上一個標識,一般是在對象的header中,將其記錄為可達對象。 - 清除階段,collector對堆內存(heap memory)從頭到尾進行線性的遍歷

    2024年02月16日
    瀏覽(27)
  • JVM——垃圾回收(垃圾回收算法+分代垃圾回收+垃圾回收器)

    JVM——垃圾回收(垃圾回收算法+分代垃圾回收+垃圾回收器)

    只要一個對象被其他對象所引用,就要讓該對象的技術加1,某個對象不再引用其,則讓它計數(shù)減1。當計數(shù)變?yōu)?時就可以作為垃圾被回收。 有一個弊端叫做循環(huán)引用,兩個的引用計數(shù)都是1,導致不能作為垃圾回收,會造成內存泄露。 java虛擬機沒有采用該算法。 該算法需要

    2024年02月12日
    瀏覽(18)
  • JVM的組件、自動垃圾回收的工作原理、分代垃圾回收過程、可用的垃圾回收器類型

    JVM的組件、自動垃圾回收的工作原理、分代垃圾回收過程、可用的垃圾回收器類型

    https://www.processon.com/diagraming/64c8aa11c07d99075d934311 https://www.oracle.com/webfolder/technetwork/tutorials/obe/java/gc01/index.html 年輕代是所有新對象被分配和老化的地方。當年輕代填滿時,這會導致minor garbage collection,minor gc會回收掉很多的游離對象。游離的年輕代很快就被收集起來。一些幸存的

    2024年02月14日
    瀏覽(65)
  • 【JVM】JVM 垃圾回收算法

    【JVM】JVM 垃圾回收算法

    目前JVM中有三種常見的垃圾回收算法,分別是:標記清除、標記整理和復制,這三種垃圾回收算法各有優(yōu)缺點,下面逐一介紹。 在讀本篇文章中,如果對JVM中哪個是垃圾,哪個不是垃圾,JVM到底是怎么知道的,請先讀下面這篇文章 【JVM】JVM 判斷對象存活算法(引用計數(shù)算法

    2024年02月09日
    瀏覽(27)
  • JVM基礎(5)——JVM垃圾回收算法

    JVM基礎(5)——JVM垃圾回收算法

    作者簡介:大家好,我是smart哥,前中興通訊、美團架構師,現(xiàn)某互聯(lián)網(wǎng)公司CTO 聯(lián)系qq:184480602,加我進群,大家一起學習,一起進步,一起對抗互聯(lián)網(wǎng)寒冬 學習必須往深處挖,挖的越深,基礎越扎實! 階段1、深入多線程 階段2、深入多線程設計模式 階段3、深入juc源碼解析

    2024年02月02日
    瀏覽(31)
  • JVM---垃圾回收算法介紹

    目錄 分代收集理論 三種垃圾回收算法 標記-清除算法(最基礎的、基本不用) 標記-復制算法 標記-整理算法 正式因為jvm有了垃圾回收機制,作為java開發(fā)者不會去特備關注內存,不像C和C++。 優(yōu)點 :開發(fā)門檻低、安全 缺點 :性能問題。c和c++可以自己操控內存等,性能更高

    2024年02月12日
    瀏覽(23)
  • 【JVM】垃圾回收算法

    【JVM】垃圾回收算法

    標記-清除算法將垃圾回收分為兩個階段,標記階段和清除階段 在標記階段首先通過GC Roots,標記所有從根節(jié)點開始的對象,未被標記的對象就是未引用的垃圾對象。然后,在清除階段,清除未被標記的對象。 適合場景: 1、存活對象較多的情況下比較高效 2、使用于老年代

    2024年01月16日
    瀏覽(66)
  • jvm垃圾回收相關的算法

    JVM主要通過以下幾種方式來判斷對象是否需要回收: 引用計數(shù)法:JVM通過引用計數(shù)器來判斷對象的引用數(shù)量,當引用數(shù)量為0時,表示對象可以被回收。 可達性分析算法:JVM通過根對象(如棧中的引用、靜態(tài)變量等)出發(fā),對對象進行可達性分析,判斷對象是否可被訪問到,

    2024年02月02日
    瀏覽(25)

覺得文章有用就打賞一下文章作者

支付寶掃一掃打賞

博客贊助

微信掃一掃打賞

請作者喝杯咖啡吧~博客贊助

支付寶掃一掃領取紅包,優(yōu)惠每天領

二維碼1

領取紅包

二維碼2

領紅包