欧美free性护士vide0shd,老熟女,一区二区三区,久久久久夜夜夜精品国产,久久久久久综合网天天,欧美成人护士h版

首頁綜合 正文
目錄

柚子快報(bào)邀請碼778899分享:java JVM垃圾判定算法

柚子快報(bào)邀請碼778899分享:java JVM垃圾判定算法

http://yzkb.51969.com/

垃圾收集技術(shù)是Java的一堵高墻。Java堆內(nèi)存中存放著幾乎所有的對象實(shí)例,垃圾收集器在對堆內(nèi)存進(jìn)行回收前,第一件事情就是要確定這些對象中哪些還存活,哪些已經(jīng)死去(即不可能再被任何途徑使用的對象)。也就是判定垃圾。通常有兩種方法:

引用計(jì)數(shù)法

引用計(jì)數(shù)法(Reference Counting)的算法是:給每個(gè)對象添加一個(gè)引用計(jì)數(shù)器,有一個(gè)引用,計(jì)數(shù)器值加1;當(dāng)引用失效,計(jì)數(shù)器值減1;任何時(shí)刻計(jì)數(shù)器值為0的對象就是不可能再被使用的。 這種方法實(shí)現(xiàn)簡單,判定效率也很高,但有個(gè)問題:它很難解決對象之間相互循環(huán)引用的問題。

package org.hbin.gc;

/**

* VM args: -XX:+PrintGC -XX:+PrintGCDetails

* @author Haley

* @version 1.0

* 2024/9/1

*/

public class ReferenceCountingGC {

private Object instance;

private byte[] array = new byte[1024 * 1024 * 5];

public static void main(String[] args) {

ReferenceCountingGC a = new ReferenceCountingGC();

ReferenceCountingGC b = new ReferenceCountingGC();

a.instance = b;

b.instance = a;

a = null;

b = null;

System.gc();

}

}

運(yùn)行上述代碼,并打印GC日志:

[GC (System.gc()) 12918K->616K(125952K), 0.0010921 secs]

[Full GC (System.gc()) 616K->459K(125952K), 0.0097270 secs]

從運(yùn)行結(jié)果可以看到,GC日志中包含12918K->616K,說明并沒有因?yàn)檫@兩個(gè)對象互相引用而不回收,也從側(cè)面說明虛擬機(jī)并不是通過引用計(jì)數(shù)法來判斷垃圾的。

根可達(dá)性分析算法

根可達(dá)性分析算法的基本思路:通過一系列稱為GC Roots的對象作為起始點(diǎn),從這些節(jié)點(diǎn)開始向下搜索,搜索路徑稱為引用鏈(Reference Chain)。當(dāng)一個(gè)對象到GC Roots沒有任何引用鏈相連(圖論:從GC Roots到這個(gè)對象不可達(dá))時(shí),則證明此對象是不可用的。 在Java中,可作為GC Roots的對象包括:

虛擬機(jī)棧(棧幀中的局部變量表)中引用的對象方法區(qū)中類靜態(tài)屬性引用的對象方法區(qū)中常量引用的對象本地方法棧中JNI(即native方法)引用的對象

JVM中GC Roots的構(gòu)成非常復(fù)雜,根據(jù)程序執(zhí)行的語義、語言特性的支持及JVM內(nèi)部優(yōu)化實(shí)現(xiàn),可以劃分為Java根、JVM根和其他根。

Java根用于找到Java程序執(zhí)行時(shí)產(chǎn)生的對象,包括:

類元數(shù)據(jù)對象:利用類加載器來跟蹤Java程序運(yùn)行時(shí)加載的類元數(shù)據(jù)對象Java對象:通過線程棧幀跟蹤Java程序的活躍對象

JVM根主要指JVM為了運(yùn)行Java程序所產(chǎn)生的一些對象,這些對象可以簡單地被認(rèn)為是全局對象。主要有:

Universe:Java程序運(yùn)行時(shí)需要一些全局對象,比如Java支持8種基本數(shù)據(jù)類型,這些基本類型的信息需要對象來描述(作為全局對象也是為了性能考慮),這些對象就存放在Universe中。Monitor:全局監(jiān)視器對象,對于Monitor對象主要是用于鎖相關(guān),可能存在只有Monitor對象引用到內(nèi)存空間的對象,所以Monitor是JVM的根之一。JNI:JVM執(zhí)行本地代碼時(shí)使用API產(chǎn)生的對象,例如通過JNI API在堆中創(chuàng)建對象,這些對象只在JNI API中使用,所以需要單獨(dú)管理這些對象。JVMTI:使用JVM提供的接口用于調(diào)試、分析Java程序。System Dictionary:JVM在設(shè)計(jì)類加載時(shí),對于基本的類,比如Java中經(jīng)常使用的基礎(chǔ)類,會通過系統(tǒng)加載器加載這些類,而這些類在運(yùn)行Java程序一直都需要,所以這些類被單獨(dú)加載,單獨(dú)標(biāo)記。Management:JVM提供的內(nèi)存管理API,用于JVM內(nèi)存的統(tǒng)計(jì)信息,在使用這些API時(shí)需要?jiǎng)?chuàng)建Java對象,所以需要標(biāo)記。AOT:在JDK9引入了提前編譯。在AOT的編譯過程中會把全局對象和編譯優(yōu)化的代碼對象放在可執(zhí)行文件中,當(dāng)執(zhí)行時(shí)會用到這些對象,所以在回收時(shí)需要標(biāo)記。

其他根主要有:

語言特性的弱引用JVM弱根,例如String.intern()產(chǎn)生的對象等。

這些根共同構(gòu)成了GC Roots集合,實(shí)際上根的確定和虛擬機(jī)運(yùn)行時(shí)密切相關(guān)。對于弱根的處理在不同的GC實(shí)現(xiàn)中也會有所不同。

當(dāng)前主流編程語言的垃圾收集器基本上都是依靠可達(dá)性分析算法來判定對象是否存活的,理論上該算法要求全過程都基于一個(gè)能保障一致性的快照中才能夠進(jìn)行分析,這意味著必須全程凍結(jié)用戶線程的運(yùn)行。要根節(jié)點(diǎn)枚舉這個(gè)步驟中,由于GC Roots相比起整個(gè)Java堆中全部的對象畢竟還是極少數(shù),且在各種優(yōu)化技巧(如OopMap)的加持下,它帶來的停頓已經(jīng)是非常短暫且相對固定(不隨堆容量而增長)??蓮腉C Roots再繼續(xù)往下遍歷,這一步驟的停頓時(shí)間就必定會與Java堆容量直接成正比例關(guān)系了:堆越大,存儲的對象越多,對象圖結(jié)構(gòu)越復(fù)雜,要標(biāo)記更多對象而產(chǎn)生的停頓時(shí)間自然就更長。 標(biāo)記階段是所有追蹤式垃圾收集算法的共同特征,如果這個(gè)階段會隨著堆變大而等比例增加停頓時(shí)間,其影響就會涉及幾乎所有的垃圾收集器。同理,如果能削減這部分停頓時(shí)間的話,那收益也將會是系統(tǒng)性的。 想解決或者降低用戶線程的停頓,要先搞清楚為什么必須在一個(gè)能保障一致性的快照上才能進(jìn)行對象圖的遍歷?這里引入三色標(biāo)記(Tri-color Marking),把垃圾收集器遍歷對象過程中遇到的對象,按照是否訪問過標(biāo)記成三種顏色:

白色:表示對象尚未被訪問過。顯然在剛開始,所有的對象都是白色的;分析結(jié)束時(shí),仍然是白色的對象,即代表不可達(dá)。黑色:表示對象已經(jīng)被垃圾收集器訪問過,且這個(gè)對象的所有引用都已經(jīng)掃描過。黑色的對象代表已經(jīng)掃描過,它是安全存活的,如果有其他對象引用指向了黑色對象,無須重新掃描一遍。黑色對象不可能直接指向某個(gè)白色對象,而不經(jīng)過灰色對象?;疑罕硎緦ο笠呀?jīng)訪問過,但這個(gè)對象上至少存在一個(gè)引用還沒有被掃描過。

如果整個(gè)掃描過程中,用戶線程是凍結(jié)的,只有收集器線程在工作,那不會有任何問題。但如果用戶線程與收集器線程是并發(fā)工作呢?收集器線程標(biāo)記過程中,用戶線程隨時(shí)可能修改任何引用關(guān)系,這可能出現(xiàn)兩種后果:

原本消亡的對象錯(cuò)誤標(biāo)記為存活:這不是好事兒,但可以容忍,只是產(chǎn)生一些浮動(dòng)垃圾,它們會逃過本次收集,下次收集再清理掉就好。原本存活的對象錯(cuò)誤標(biāo)記為消亡:這是非常致命的,程序會因此發(fā)生錯(cuò)誤。

整個(gè)掃描標(biāo)記過程和異常情況如下: Wilson于1994年在理論上證明:當(dāng)且僅當(dāng)以下兩個(gè)條件同時(shí)滿足時(shí),會產(chǎn)生“對象消失”的問題,即原本應(yīng)該是黑色的對象被誤標(biāo)為白色:

賦值時(shí)插入了一條或多條從黑色對象到白色對象的新引用賦值時(shí)刪除了全部從灰色對象到該白色對象的直接或間接引用 因此,要解決這個(gè)問題,只需要破壞這兩個(gè)條件中的任意一個(gè)即可。由此,分別產(chǎn)生了兩種解決方案:增量更新:Incremental Update。增量更新要破壞的是第一個(gè)條件,當(dāng)黑色對象插入新的指向白色對象的引用關(guān)系時(shí),就將這個(gè)新插入的引用記錄下來,等并發(fā)掃描結(jié)束之后,再將這些記錄過的引用關(guān)系中的黑色對象為根,重新掃描一次。也簡化理解:黑色對象一旦新插入了指向白色對象的引用之后,它就變回灰色對象了。原始快照:Snapshot At The Beginning, STAB。原始快照要破壞的是第二個(gè)條件,當(dāng)灰色對象要?jiǎng)h除指向白色對象的引用關(guān)系時(shí),就將這個(gè)要?jiǎng)h除的引用記錄下來,在并發(fā)掃描結(jié)束之后,再將這些記錄過的引用關(guān)系中的灰色對象為根,重新掃描一次。也可以簡化理解:無論引用關(guān)系刪除與否,都會按照剛剛開始掃描那一刻的對象圖快照來進(jìn)行搜索。 以上無論是對引用關(guān)系記錄的插入還是刪除,虛擬機(jī)的記錄操作都是通過寫屏障來實(shí)現(xiàn)的。實(shí)際上在并發(fā)標(biāo)記的時(shí)候?qū)?yīng)有兩種不同的實(shí)現(xiàn),分別是讀屏障和寫屏障。屏障技術(shù)是在讀或?qū)懖僮鲿r(shí)執(zhí)行一段代碼,其目的是調(diào)整對象的顏色從而保證正確性。但是讀操作遠(yuǎn)多于寫操作,所以讀屏障的效率一般低于寫屏障的效率。 在HotSpot虛擬機(jī)中,兩種解決方案都有實(shí)際應(yīng)用。例如,CMS是基于增量更新來做并發(fā)標(biāo)記的,G1、Shenandoah則是用原始快照來實(shí)現(xiàn)。

引用分類

在JDK1.2之前,引用的定義是:如果reference類型的數(shù)據(jù)中存儲的數(shù)值代表的是另外一塊內(nèi)存的起始地址,就稱這塊內(nèi)存代表著一個(gè)引用。 JDK1.2之后,Java對引用的概念進(jìn)行了擴(kuò)充,將引用分為四種:強(qiáng)引用、軟引用、弱引用、虛引用,這四種引用強(qiáng)度依次逐漸減弱。最后還有一個(gè)特殊的引用FinalReference。

強(qiáng)引用:Strong Reference,代碼中普遍存在的,類似Object obj = new Object()這類的引用。只要引用還存在,垃圾收集器永遠(yuǎn)不會回收掉這些對象。軟引用:Soft Reference,用來描述一些還有用但非必需的對象。在系統(tǒng)將要發(fā)生內(nèi)存溢出之前,將會把這些對象列進(jìn)回收范圍之中進(jìn)行二次回收。如果這次回收還不骨足夠的內(nèi)存,才會拋出內(nèi)存溢出異常。相關(guān)Java類:SoftReference弱引用:Weak Reference,也用來描述非必需對象,但它的強(qiáng)度比軟引用更弱一些。當(dāng)垃圾回收器工作時(shí),無論當(dāng)前內(nèi)存是否足夠,都會回收掉弱引用關(guān)聯(lián)的對象。相關(guān)Java類:WeakReference。虛引用:也稱幽靈引用或幻影引用,它是最弱的一種引用關(guān)系。虛引用不影響對象的生成時(shí)間,也無法通過虛引用來取得一個(gè)對象實(shí)例。設(shè)置虛引用的唯一目的是能在這個(gè)對象被垃圾回收器回收時(shí)收到一個(gè)系統(tǒng)通知。相關(guān)Java類:PhantomReference。 兩個(gè)常用子類:

sum.misc.Cleaner:用于DirectByteBuffer對象回收的時(shí)候?qū)τ诙淹鈨?nèi)存的回收。java.lang.ref.Cleaner:用于在被引用的對象回收的時(shí)候觸發(fā)一個(gè)動(dòng)作,在OpenJDK9中將完全替代Object.finalize()方法。 最終引用:FinalReference,包權(quán)限,開發(fā)者無法直接繼承擴(kuò)展。它只有一個(gè)子類Finalizer,并且由final修飾,無法繼承擴(kuò)展。由于構(gòu)造方法是私有的,所以只能由HotSpot VM通過調(diào)用register()方法將被引用的對象封裝為Finalizer對象。 在類加載過程中,如果當(dāng)前類重寫了finalize(),則其對象會被封裝為FinalReference對象,這樣FinalReference對象的referent字段就指向了當(dāng)前類的對象。

Finalizer對象鏈會保存全部的只存在FinalizerReference引用且沒有執(zhí)行過finalize()方法的Finalizer對象,防止Finalizer對象在其引用的對象之前被GC回收。在GC過程中,如果發(fā)現(xiàn)referent對象不可達(dá),則Finalizer對象會添加到queue列表中,所有在queue隊(duì)列中的對象都會調(diào)用finalize()方法。

柚子快報(bào)邀請碼778899分享:java JVM垃圾判定算法

http://yzkb.51969.com/

推薦鏈接

評論可見,查看隱藏內(nèi)容

本文內(nèi)容根據(jù)網(wǎng)絡(luò)資料整理,出于傳遞更多信息之目的,不代表金鑰匙跨境贊同其觀點(diǎn)和立場。

轉(zhuǎn)載請注明,如有侵權(quán),聯(lián)系刪除。

本文鏈接:http://gantiao.com.cn/post/19420556.html

發(fā)布評論

您暫未設(shè)置收款碼

請?jiān)谥黝}配置——文章設(shè)置里上傳

掃描二維碼手機(jī)訪問

文章目錄