摘要:這個(gè)算法看似不錯而且簡單,不過存在這一個(gè)致命傷當(dāng)兩個(gè)對象互相引用的時(shí)候,就永遠(yuǎn)不會被回收于是引用計(jì)數(shù)算法就永遠(yuǎn)回收不了這兩個(gè)對象,下面介紹另一種算法。
前言
? 如果要問Java與其他編程語言最大的不同是什么,我第一個(gè)想到的一定就是Java所運(yùn)行的JVM所自帶的自動垃圾回收機(jī)制,以下是我學(xué)習(xí)JVM垃圾回收機(jī)制整理的筆記,希望能對讀者有一些幫助。
? 如何判斷對象已死?有兩種算法
引用計(jì)數(shù)算法? 給對象添加一個(gè)計(jì)數(shù)器,每當(dāng)有一個(gè)地方引用它時(shí),計(jì)數(shù)器的值就加一,當(dāng)引用失效的時(shí)候,計(jì)數(shù)器就減一 ,任何時(shí)刻計(jì)數(shù)器為0的對象就是不可能再被使用的時(shí)候。
? 這個(gè)算法看似不錯而且簡單,不過存在這一個(gè)致命傷(當(dāng)兩個(gè)對象互相引用的時(shí)候,就永遠(yuǎn)不會被回收)
public class Obj{ public Object instance=null; } Obj a=new Obj(); Obj b=new Obj(); a.instance=b; b.instance=a; a=null; b=null;
于是引用計(jì)數(shù)算法就永遠(yuǎn)回收不了這兩個(gè)對象,下面介紹另一種算法。
可達(dá)性分析算法? 通過一系列被稱為“GC Roots”的對象作為起始點(diǎn),從這些接點(diǎn)向下搜索,搜索所走過的路徑稱為引用鏈,當(dāng)一個(gè)對象與任何一個(gè)引用鏈沒有關(guān)聯(lián)的時(shí)候則可以被回收。
Java中,可作為GC Roots的對象包括下面集中
虛擬機(jī)棧(棧幀中的本地變量表)中引用的對象
方法區(qū)中類靜態(tài)屬性引用的對象
方法區(qū)中常量引用的對象
本地方法棧中JNI(Native方法)引用的對象
淺談引用? 無論是計(jì)數(shù)器算法還是可達(dá)性分析算法,都與“引用”有關(guān),下面是Java中4種引用,強(qiáng)度依次減弱
? 強(qiáng)引用就是我們平時(shí)最熟悉的
Object obj=new Object();
? 只要強(qiáng)引用還存在,垃圾收集器永遠(yuǎn)不會回收掉被引用的對象。
? 發(fā)生gc的時(shí)候,如果JVM內(nèi)存充足則不回收,用SoftReference類來實(shí)現(xiàn)軟引用。展示一個(gè)例子
SoftReference
? 以下是輸出
before gc java.lang.Object@2752f6e2 after gc java.lang.Object@2752f6e2
可以看到軟引用的對象依然還在。
? 一旦發(fā)生gc,無論JVM內(nèi)存充足與否,都會回收掉,用WeakReference類來實(shí)現(xiàn)弱引用。展示一個(gè)例子
WeakReference
? 以下是輸出
before gc java.lang.Object@2752f6e2 after gc null
弱引用的對象已經(jīng)被回收!
? 虛引用也稱為幽靈引用或者幻影引用,是最弱的一種引用,無法通過虛引用來取得一個(gè)對象實(shí)例,擁有虛引用的對象可以在任何時(shí)候被垃圾回收器回收。唯一的目的就是能在當(dāng)這個(gè)對象被回收的時(shí)候收到一個(gè)系統(tǒng)通知,可用PhantomReference類實(shí)現(xiàn)虛引用。
回收方法區(qū)? 垃圾回收大多發(fā)生在Heap(堆區(qū)),因?yàn)樵诜椒▍^(qū)進(jìn)行垃圾回收效率較低,要判定一個(gè)類是否是“無用的類”條件比較苛刻,類需要同時(shí)滿足下面3個(gè)條件才能算是無用的類
該類所有實(shí)例已經(jīng)被回收,堆中不存在該類任何實(shí)例
加載該類的ClassLoader已經(jīng)被回收
該類對應(yīng)的java.lang.Class對象沒有在任何地方被引用,無法在任何地方通過反射訪問該類的方法。
什么時(shí)候回收?when?? 首先,GC又分為minor GC 和 Full Gc(也稱為Major GC)。Java 堆內(nèi)存分為新生代和老年代,新生代中又分為1個(gè)Eden區(qū)域 和兩個(gè) Survivor區(qū)域。
那么對于 Minor GC 的觸發(fā)條件:大多數(shù)情況下,直接在 Eden 區(qū)中進(jìn)行分配。如果 Eden區(qū)域沒有足夠的空間,那么就會發(fā)起一次 Minor GC;對于 Full GC(Major GC)的觸發(fā)條件:也是如果老年代沒有足夠空間的話,那么就會進(jìn)行一次 Full GC。
注意:上面所說的只是一般情況,實(shí)際上,需要考慮一個(gè)空間分配擔(dān)保的問題:
在發(fā)生Minor GC之前,虛擬機(jī)會先檢查老年代最大可用的連續(xù)空間是否大于新生代所有對象的總空間。如果大于則進(jìn)行Minor GC,如果小于則看HandlePromotionFailure設(shè)置是否允許擔(dān)保失?。ú辉试S則直接Full GC)。如果允許,那么會繼續(xù)檢查老年代最大可用的連續(xù)空間是否大于歷次晉升到老年代對象的平均大小,如果大于則嘗試Minor GC(如果嘗試失敗也會觸發(fā)Full GC),如果小于則進(jìn)行Full GC。
System.gc()就是指的Full GC
但是,具體程序執(zhí)行到什么位置才會自動gc,這兒提兩個(gè)概念Safepoint和SafeRegion。
安全點(diǎn)(Safepoint)? 安全點(diǎn)的選定是以程序“是否具有讓程序長時(shí)間執(zhí)行的特征”為標(biāo)準(zhǔn)選定的,“長時(shí)間執(zhí)行”最明顯的特征就是
指令序列復(fù)用
循環(huán)跳轉(zhuǎn)
異常跳轉(zhuǎn)等
對于Safepoint如何在GC發(fā)生時(shí)讓所有線程都跑到最近的安全點(diǎn)上停下來,有兩種方案搶先式中斷和主動式中斷。
搶先式中斷:不需要線程執(zhí)行的代碼主動配合,GC發(fā)生時(shí),首先把所有線程全部中斷,如果發(fā)現(xiàn)有線程中斷的地方不在安全點(diǎn)上,就恢復(fù)線程,讓它“跑到”安全點(diǎn)上。(這種方案幾乎很少使用)
主動式中斷 :當(dāng)GC需要中斷線程的時(shí)候,不直接對線程操作,僅僅簡單設(shè)置一個(gè)標(biāo)志,各個(gè)線程執(zhí)行時(shí)主動去輪詢這個(gè)標(biāo)志,發(fā)現(xiàn)為true就自動掛起,輪詢的地方和安全點(diǎn)是重合的。
安全區(qū)域(Saferegion)? 當(dāng)線程處于Sleep狀態(tài)或者Blocked狀態(tài),這時(shí)候線程無法響應(yīng)JVM的中斷請求,“走”到安全的地方去中斷掛起,JVM也顯然不太可能等待線程重新被分配CPU時(shí)間,這時(shí)候就需要安全區(qū)域來解決。
? 當(dāng)線程執(zhí)行到Safe Region中的代碼時(shí),首先標(biāo)識自己已經(jīng)進(jìn)入了Safe Region,當(dāng)JVM在發(fā)起GC時(shí),就不用管標(biāo)識自己為Safe Region狀態(tài)的線程了。在線程要離開Safe Region時(shí),他要檢查系統(tǒng)是否完成了整個(gè)GC過程,如果完成了,線程就繼續(xù)執(zhí)行,否則必須等待直到收到可以安全離開Safe Region的信號為止。
如何回收?how? 標(biāo)記-清除 算法? 算法分為標(biāo)記和清除兩個(gè)階段,首先標(biāo)記出所有需要回收的對象,在標(biāo)記完成后統(tǒng)一回收所有被標(biāo)記的對象。他的不足主要有兩個(gè):
效率問題:標(biāo)記和清除兩個(gè)過程的效率都不高
空間問題:標(biāo)記清除之后,會產(chǎn)生大量不連續(xù)的內(nèi)存碎片,空間碎片太多可能會導(dǎo)致之后分配大對象時(shí),無法找到足夠的連續(xù)內(nèi)存而提前觸發(fā)GC
復(fù)制算法? 復(fù)制算法可以將容量劃分為大小相等的兩塊,每次只使用其中的一塊,當(dāng)一塊內(nèi)存被用完了就將還存活的對象一次復(fù)制到另一塊內(nèi)存上,然后把已經(jīng)使用過的內(nèi)存一次清理掉。不過因此內(nèi)存縮小為原來的一半,代價(jià)過高。
? 現(xiàn)在的商業(yè)虛擬機(jī)普遍采用這種算法來回收新生代,將新生代分為較大的一塊Eden空間和兩塊較小的Survivor空間,HotSpot默認(rèn)其比例為8:1:1,使用時(shí),每次使用Eden加上其中一塊Survivor,回收時(shí),將Eden和Survivor中還存活的對象一次性復(fù)制到另一塊Survivor中,最后清理掉Eden和剛才用過的Survivor區(qū)。
標(biāo)記-整理 算法? 標(biāo)記過程與“標(biāo)記-清除”算法一樣,然后讓所有存活的對象都向同一端移動,然后直接清理端邊界以外的內(nèi)存。
分代收集算法? 將Java堆分為新生代和老年代,在新生代中每次垃圾回收都有大批對象死去,少量存活,可以使用復(fù)制算法,而老年代中對象存活率高沒有額外的空間對它進(jìn)行分配擔(dān)保,必須使用“標(biāo)記-整理”或者“標(biāo)記-清理”算法來回收。
談?wù)凣1垃圾收集器? 在最近更新的JDK9中,JVM默認(rèn)的垃圾收集器切換成了G1,那么G1有什么特點(diǎn)呢?總結(jié)以下最大的四個(gè)特點(diǎn)
并行與并發(fā):G1能充分利用多CPU、多核環(huán)境下的硬件優(yōu)勢,使用多個(gè)CPU,(CPU或者CPU核心)來縮短Stop-The-World停頓的時(shí)間,部分其他收集器需要停頓Java線程執(zhí)行的GC動作,G1可以通過并發(fā)的方式讓Java繼續(xù)執(zhí)行。
分代收集:G1不需要其他收集器配合就能獨(dú)立管理整個(gè)GC堆,G1采用不同的方式去處理新創(chuàng)建的對象、已經(jīng)存活一段時(shí)間的對象、熬過多次GC的舊對象以獲取更好的收集效果。
空間整合:G1從整體來看是基于“標(biāo)記-整理”算法,局部上來看是基于復(fù)制算法,這兩種算法都不會產(chǎn)生內(nèi)存碎片,有利于程序長時(shí)間運(yùn)行。
可預(yù)測的停頓:G1除了追求低停頓外,還能建立可預(yù)測的停頓時(shí)間模型,能讓使用者明確指定在一個(gè)長度為M毫秒的時(shí)間片段內(nèi),消耗在垃圾收集上的時(shí)間不超過N毫秒。
資料來源《深入理解Java虛擬機(jī)》P61-P84
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/69424.html
摘要:還記得剛開始學(xué)習(xí)的時(shí)候,內(nèi)存管理前端掘金作為一門高級語言,并不像低級語言那樣擁有對內(nèi)存的完全掌控。第三方庫的行代碼內(nèi)實(shí)現(xiàn)一個(gè)前端掘金前言本文會教你如何在行代碼內(nèi),不依賴任何第三方的庫,用純實(shí)現(xiàn)一個(gè)。 (譯) 如何使用 JavaScript 構(gòu)建響應(yīng)式引擎 —— Part 1:可觀察的對象 - 掘金原文地址:How to build a reactive engine in JavaSc...
摘要:還記得剛開始學(xué)習(xí)的時(shí)候,內(nèi)存管理前端掘金作為一門高級語言,并不像低級語言那樣擁有對內(nèi)存的完全掌控。第三方庫的行代碼內(nèi)實(shí)現(xiàn)一個(gè)前端掘金前言本文會教你如何在行代碼內(nèi),不依賴任何第三方的庫,用純實(shí)現(xiàn)一個(gè)。 (譯) 如何使用 JavaScript 構(gòu)建響應(yīng)式引擎 —— Part 1:可觀察的對象 - 掘金原文地址:How to build a reactive engine in JavaSc...
摘要:用戶態(tài)不能干擾內(nèi)核態(tài)所以指令就有兩種特權(quán)指令和非特權(quán)指令不同的狀態(tài)對應(yīng)不同的指令。非特權(quán)指令所有程序均可直接使用。用戶態(tài)常態(tài)目態(tài)執(zhí)行非特權(quán)指令。 這是我今年從三月份開始,主要的大廠面試經(jīng)過,有些企業(yè)面試的還沒來得及整理,可能有些沒有帶答案就發(fā)出來了,還請各位先思考如果是你怎么回答面試官?這篇文章會持續(xù)更新,請各位持續(xù)關(guān)注,希望對你有所幫助! 面試清單 平安產(chǎn)險(xiǎn) 飛豬 上汽大通 浩鯨科...
摘要:執(zhí)行過程引擎會加載源代碼,把它分解成字符串又叫做分詞,再把這些字符串轉(zhuǎn)換成編譯器可以理解的字節(jié)碼,然后執(zhí)行這些字節(jié)碼。接著四個(gè)進(jìn)程開始參與進(jìn)來,分析和執(zhí)行解析器所生成的字節(jié)碼。 JavaScript運(yùn)行原理 知其然,也要知其所以然,這里主要談一談對JavaScript運(yùn)行原理的理解。 JAVA虛擬機(jī) 首先我們從JAVA虛擬機(jī)說起。 首先說一下為什么要做成虛擬機(jī),因?yàn)闄C(jī)器不同,如果沒有虛...
摘要:目錄介紹問題匯總具體問題好消息博客筆記大匯總年月到至今,包括基礎(chǔ)及深入知識點(diǎn),技術(shù)博客,學(xué)習(xí)筆記等等,還包括平時(shí)開發(fā)中遇到的匯總,當(dāng)然也在工作之余收集了大量的面試題,長期更新維護(hù)并且修正,持續(xù)完善開源的文件是格式的同時(shí)也開源了生活博客,從年 目錄介紹 00.Java問題匯總 01.具體問題 好消息 博客筆記大匯總【16年3月到至今】,包括Java基礎(chǔ)及深入知識點(diǎn),Android技...
閱讀 3195·2023-04-25 17:19
閱讀 634·2021-11-23 09:51
閱讀 1359·2021-11-08 13:19
閱讀 793·2021-09-29 09:34
閱讀 1695·2021-09-28 09:36
閱讀 1506·2021-09-22 14:59
閱讀 2722·2019-08-29 16:38
閱讀 2066·2019-08-26 13:40