自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

JVM如何判斷哪些對象可以回收?

云計(jì)算 虛擬化
我們知道在JVM內(nèi)存中,實(shí)例對象基本都是存在于堆中的,那總不能無期限的往里面放吧,一些用不著的對象就需要隨時(shí)回收掉,這樣才能保證這個(gè)內(nèi)存的均衡性,才能保證JVM的正常運(yùn)行。

[[387518]]

前言

我們上一篇分析的是JVM的內(nèi)存分布,分為堆內(nèi)存、虛擬機(jī)棧、本地方法棧、方法區(qū)以及程序計(jì)數(shù)器等主要區(qū)域;各個(gè)區(qū)域的特點(diǎn)我也就不啰嗦了,想看的給大家直通車:

大魚今天在家本來是閑暇的一天,很舒適,結(jié)果這個(gè)時(shí)候,媽媽敲門進(jìn)來我房間了,咨詢我有沒有時(shí)間幫忙打掃一下父母的房間;(沒有時(shí)間

當(dāng)然我不能這么說了,我是個(gè)炒雞孝順的好孩子,當(dāng)然了,媽媽,當(dāng)然有時(shí)間了啊,now go,我的乖乖,這么亂的屋子,不對啊,平時(shí)都是很干凈的啊(內(nèi)心想逃,后悔,想拒絕

不對啊,媽,為什么房間這么亂啊,這有的東西我也不知道要不要扔掉啊,瞬間難到我了,你們生活中有沒有遇到過類似的煩惱?

或者有沒有遇到糾結(jié)一個(gè)東西要不要扔掉的時(shí)候,那時(shí)候你是如何做的呢?

我們知道在JVM內(nèi)存中,實(shí)例對象基本都是存在于堆中的,那總不能無期限的往里面放吧,一些用不著的對象就需要隨時(shí)回收掉,這樣才能保證這個(gè)內(nèi)存的均衡性,才能保證JVM的正常運(yùn)行

那么問題來了,JVM如何知道哪些對象該回收、哪些不該回收,就像剛才大魚不知道爸媽房間哪些東西該收拾、哪些不該收拾一個(gè)道理的,其實(shí)在JVM中是有兩種解決辦法的,分別是引用計(jì)數(shù)法和可達(dá)性分析法兩種方法,來確定這些對象之中哪些是存活著的、哪些是已經(jīng)死去的(不可能再被任何途徑使用的對象)

問題明白了,下面就是來解決這個(gè)問題了,沖吧,干飯人

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

這個(gè)其實(shí)很簡單了,重點(diǎn)就是計(jì)數(shù);給對象添加一個(gè)引用計(jì)數(shù)器,每引用一次,計(jì)數(shù)器加一;引用失效的時(shí)候,計(jì)數(shù)器減一;當(dāng)計(jì)數(shù)器為0 的時(shí)候,則認(rèn)為不可能被再次使用了;

我覺得不需要大魚多解釋了應(yīng)該,這個(gè)應(yīng)該及其好理解,但是,這種方法存在一個(gè)致命的問題:無法解決對象相互循環(huán)引用的問題

解釋下這個(gè)循環(huán)引用問題

一起來看看下面這個(gè)例子

  1. public class ReferenceCountingGC { 
  2.  
  3.     public Object instance = null
  4.      
  5.     private byte[] bigSize = new byte[2 * 1024 * 1024]; 
  6.     public static void main(String[] args) { 
  7.         ReferenceCountingGC o1 = new ReferenceCountingGC(); 
  8.         ReferenceCountingGC o2 = new ReferenceCountingGC(); 
  9.         o1.instance = o2; 
  10.         o2.instance = o1; 
  11.         o1 = null
  12.         o2 = null
  13.         //假設(shè)在這行發(fā)生了GC,o1和o2是否被回收 
  14.         System.gc(); 
  15.     } 

上面例子中o1和o2對象都分別將對方作為自己的屬性注入,這也就是形成了所謂的循環(huán)引用;最后o1和o2對象都置為null,也就是棧中不再指向堆中的實(shí)例對象地址,但是他們還是會互相引用,所以不會被GC回收

再來看個(gè)圖解版,加深理解

剛new的o1和o2對象是這個(gè)樣子的:

分別引用了雙方之后是這樣子的狀態(tài):

最后置為null變成這個(gè)樣子的:

是的,沒錯(cuò),最后就變成了如上圖所示的尷尬境地,對象1和對象2在內(nèi)部互相引用,永遠(yuǎn)失效不了,導(dǎo)致GC通過引用計(jì)數(shù)法判斷他們的引用計(jì)數(shù)的時(shí)候,永遠(yuǎn)無法判斷為0,也就是無法回收咯,不就造成了內(nèi)存泄漏了嗎

可達(dá)性分析法

上面說的引用計(jì)數(shù)法有缺點(diǎn),而且這個(gè)問題還不小,所以現(xiàn)在使用這種方式來作為判斷對象是否存活標(biāo)準(zhǔn)的比較少,多數(shù)使用的是另一種,可達(dá)性分析法;

先來解釋下可達(dá)性分析法

基本思路就是通過一系列的”GC Roots“的對象作為起始點(diǎn),從這些節(jié)點(diǎn)開始向下搜索,搜索所走過的路徑就是引用鏈,當(dāng)一個(gè)對象到GC Roots沒有任何的引用鏈可達(dá)的時(shí)候,則證明這個(gè)對象是不可用的

什么意思呢?來個(gè)白話文版本的,就是選擇一系列的基準(zhǔn)點(diǎn),這個(gè)點(diǎn)能通過引用鏈連接到的對象就被認(rèn)為是可用的,只要是無法到達(dá)的,都被認(rèn)為是不可用的,這個(gè)不可用并不一定代表對象死亡,只代表對象無法觸達(dá),無法再次引用

這就像遞歸定義的關(guān)系一樣,如果只定義了遞歸項(xiàng)而不定義初始項(xiàng)的話,關(guān)系也就無從成立,無從開始;如果初始項(xiàng)定義漏掉了內(nèi)容的話,遞推的結(jié)果也會隨之而漏掉;

什么是GC Roots

垃圾回收時(shí),JVM會首先找到所有的GC Roots,這個(gè)過程叫做枚舉根節(jié)點(diǎn),這個(gè)過程需要暫停用戶線程,也就是stop the world;然后再從GC Roots這些根節(jié)點(diǎn)向下搜索,可達(dá)的對象保留,不可達(dá)的便會回收掉

那么,到底什么是GC Roots呢?

GC Roots就是對象,就是JVM確定當(dāng)前絕對不能回收的對象,只有找到這種對象,后面的搜索才會有意義,不能被回收的對象所依賴的對象也就必然不能回收

GC Roots是一種特殊的對象,是Java程序在運(yùn)行過程中所必須的對象,而且必須是根對象

哪些對象可以作為GC Roots

基本可以作為GC Roots的對象基本分為兩大類:全局對象和執(zhí)行上下文;

全局對象

  • 方法區(qū)靜態(tài)屬性引用的對象:全局對象的一種,Class對象本身很難被回收,回收的條件也是很苛刻,只要Class不被回收,靜態(tài)成員不會被回收
  • 方法區(qū)常量池引用的對象:全局對象,比如字符串常量池,常量初始化之后不會再次改變

執(zhí)行上下文對象

  • 方法棧的棧幀本地變量表引用的對象:線程方法執(zhí)行的時(shí)候,會將方法打包成一個(gè)棧幀入棧執(zhí)行,方法里得到的局部變量會存放到本地變量表中,只要方法未執(zhí)行完,還沒出棧,即本地變量表還會被訪問,GC不應(yīng)該回收
  • JNI本地方法棧引用的對象:和上面同樣的道理
  • 被同步鎖持有的對象:被synchronized鎖住的對象不可回收,否則鎖就失效了,那鎖就沒意義了

不可達(dá)的對象一定會回收嗎?(緩刑階段)

其實(shí)被判定為 不可達(dá)的對象,也不一定是”非死不可“的,還有一次復(fù)活機(jī)會,這時(shí)是處于緩刑階段,要真正宣告一個(gè)對象死亡,至少要經(jīng)歷再次標(biāo)記過程(其實(shí)就是finalize方法在搞怪)

我們在電視中也是經(jīng)常見到類似的場景,一個(gè)人被判定死刑了,午時(shí)已到,立即執(zhí)行,一般這個(gè)時(shí)候就會出來一個(gè)飛刀,刀下留人,皇上有旨;也有可能是一個(gè)飛刀,直接二話不說,噼里啪啦一頓操作,把人救走,是不是很熟悉

沒錯(cuò),這個(gè)過程就是finalize的內(nèi)部過程,讓被判定死刑的犯人”重獲新生“

標(biāo)記的前提是對象在進(jìn)行可達(dá)性分析后發(fā)現(xiàn)沒有與GC Roots相連接的引用鏈

第一次標(biāo)記

篩選的條件是這個(gè)對象是否有必要執(zhí)行finalize()方法;若對象未重寫這個(gè)方法或者已被虛擬機(jī)調(diào)用過,虛擬機(jī)則認(rèn)為沒有必要執(zhí)行,對象被回收

第二次標(biāo)記

若這個(gè)對象有必要執(zhí)行finalize方法,則這個(gè)對象會被放到一個(gè)F-Queue隊(duì)列中,并在稍后由虛擬機(jī)自動(dòng)創(chuàng)建的一個(gè)低優(yōu)先級的finalizer線程去執(zhí)行;

這里的執(zhí)行指的是虛擬機(jī)會觸發(fā)這個(gè)方法,但是不保證運(yùn)行完成,這樣做的原因是這個(gè)方法執(zhí)行緩慢,也可能出現(xiàn)死循環(huán),嚴(yán)重可能會導(dǎo)致回收系統(tǒng)崩潰

finalize是對象逃脫死亡命運(yùn)的最后一次機(jī)會,稍后GC會對F-Queue中的對象進(jìn)行二次標(biāo)記,如果在這里面重新和GC Roots掛上引用關(guān)系,則可以逃脫被回收的命運(yùn);否則,就肯定GG了

方法區(qū)的回收

很多人認(rèn)為方法區(qū)沒有垃圾回收,Java虛擬機(jī)規(guī)范中也確實(shí)說過可以不要求虛擬機(jī)在方法區(qū)實(shí)現(xiàn)垃圾收集,而且在方法區(qū)中的垃圾回收的性價(jià)比一般比較低,在上面說的堆中進(jìn)行一次垃圾回收會回收70—95的空間,而永久代中的垃圾回收的效率遠(yuǎn)低于此

方法區(qū)中的垃圾回收主要是兩部分:廢棄常量和無用的類;廢棄常量的回收和Java堆中的對象類似,不多說了

但是判斷一個(gè)類是否是無用的類,則條件比較苛刻,需要滿足三個(gè)條件:

  • 該類的所有實(shí)例都已經(jīng)被回收,即Java堆中無該類的任何實(shí)例
  • 加載該類的ClassLoader已經(jīng)被回收
  • 該類對應(yīng)的java.lang.Class對象沒有在任何地方被引用,無法在任何地方通過反射訪問到該類的方法

虛擬機(jī)規(guī)范中說的是滿足上面三個(gè)條件,便可以對無用的類進(jìn)行回收,但是并不是必然回收;是否對類對類進(jìn)行回收,可以根據(jù)虛擬機(jī)提供的參數(shù)來進(jìn)行控制

在大量使用反射、動(dòng)態(tài)代理、CGLib等ByteCode框架、動(dòng)態(tài)生成JSP以及OSGI這類頻繁自定義ClassLoader的場景都需要虛擬機(jī)具備類的卸載功能,以保證永久代不會溢出

我愛總結(jié)

我愛總結(jié)之JVM如何判斷哪些對象可以回收,總結(jié)很重要,整理思路,記得后續(xù)的溫故而知新,GitHub地址在下面,我會把所有原創(chuàng)技術(shù)文章放到上面,持續(xù)不斷的更新

引用計(jì)數(shù)法:存在循環(huán)引用的致命問題

可達(dá)性分析法:以GC Roots作為起點(diǎn),可以達(dá)到的就不可回收,不可達(dá)到的暫定認(rèn)為”死亡“;但是不是非死不可,有通過finalize方法加重新連接引用鏈的方法,讓一個(gè)對象重新復(fù)活;但是不保證執(zhí)行完成,這種方法是不靠譜的,也是不建議使用的

好了,以上就是全部內(nèi)容了,我是小魚仙,你們的學(xué)習(xí)成長小伙伴

我希望有一天能夠靠寫字養(yǎng)活自己,現(xiàn)在還在磨練,這個(gè)時(shí)間可能會有很多年,感謝你們做我最初的讀者和傳播者。請大家相信,只要給我一份愛,我終究會還你們一頁情的。

 

 

責(zé)任編輯:武曉燕 來源: 大魚仙人
相關(guān)推薦

2021-01-21 08:00:25

JVM

2012-02-09 10:26:33

JVMJava

2023-08-08 10:29:55

JVM優(yōu)化垃圾回收

2022-01-20 10:34:49

JVM垃圾回收算法

2017-08-04 10:53:30

回收算法JVM垃圾回收器

2010-09-26 16:42:04

JVM內(nèi)存組成JVM垃圾回收

2010-09-27 09:01:26

JVM分代垃圾回收

2022-06-22 09:54:45

JVM垃圾回收Java

2022-03-21 11:33:11

JVM垃圾回收器垃圾回收算法

2010-09-27 13:41:22

JVM內(nèi)存回收

2010-09-25 15:33:19

JVM垃圾回收

2009-12-30 10:14:29

JVM垃圾回收

2023-12-07 12:21:04

GCJVM垃圾

2022-06-10 07:13:29

JVM垃圾回收

2021-11-05 15:23:20

JVM回收算法

2009-12-25 16:15:31

JVM垃圾回收算法

2022-06-07 07:10:40

MinorGCMajorGCFullGC

2020-04-22 21:44:18

Java虛擬機(jī)算法

2017-04-25 14:39:55

JVM內(nèi)存Java

2010-09-16 15:10:24

JVM垃圾回收機(jī)制
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號