深入理解Java虛擬機之垃圾收集算法篇

這篇文章將講解垃圾回收的概念以及對那些區(qū)域進行垃圾回收割笙,最后講解幾種常見的垃圾回收算法咸灿。

概述

什么叫垃圾收集器郑趁?

需要思考GC需要完成的3件事情:

  • 哪些內(nèi)存需要回收齐饮?
  • 什么時候需要回收纬凤?
  • 如何回收福贞?

下面介紹一下Java內(nèi)存運行時區(qū)域的各個部分,為什么有些區(qū)域需要回收停士,有些區(qū)域不需要回收挖帘?以及怎么去回收?

  • 1恋技、程序計數(shù)器拇舀、虛擬機棧、本地方法棧3個區(qū)域隨線程而生蜻底,隨線程而滅骄崩;棧中的棧幀隨方法的進入和退出而有條不絮地執(zhí)行著出棧和入棧操作。因此這幾個區(qū)域的內(nèi)存分配和回收都具備確定性薄辅,在這幾個區(qū)域就不需要過多考慮回收的問題要拂,因為方法結(jié)束或者線程結(jié)束時,內(nèi)存自然就跟隨著回收了站楚。
  • 2宇弛、Java堆方法區(qū)則不一樣,一個接口中的多個實現(xiàn)類需要的內(nèi)存可能不一樣源请,一個方法中的多個分支需要的內(nèi)存也可能不一樣,我們只有在程序處于運行期間時才能知道會創(chuàng)建哪些對象,這部分內(nèi)存的分配和回收都是動態(tài)的谁尸,垃圾收集器所關(guān)注的是這部分內(nèi)存舅踪。

對象已死嗎?

在堆里面存放著Java世界中幾乎所有的對象實例良蛮,垃圾收集器在堆進行回收前抽碌,第一件事情就是確定這些對象之中哪些還“存活”著,哪些已經(jīng)“死去”(即不能再被任何途徑使用的對象)

引用計數(shù)算法

很多教科書判斷對象是否存活的算法是這樣的:給對象中添加一個引用計數(shù)器决瞳,每當(dāng)有一個地方引用它時货徙,計數(shù)器值加1;當(dāng)引用失效時皮胡,計數(shù)器就減1痴颊;任何時刻計數(shù)器為0的對象就是不可能在被使用的。

可達性分析算法

通過可達性分析算法(Reachability Analysis)來判定對象是否存活的屡贺。

基本思路:通過一系列的稱為GC Roots 的對象作為起始點蠢棱,從這些起始點往下搜索,搜索走過的路徑稱為引用鏈甩栈,當(dāng)一個對象和GC Roots沒有任何引用鏈(即GC Roots到這個對象時不可達的)泻仙,說明對象時無用的。

[圖片上傳失敗...(image-bcae71-1521271966954)]

在Java中可作為GC Roots的對象有下面幾種:

  • 虛擬機棧(棧幀中的本地變量表)中引用的對象量没。
  • 方法區(qū)中類靜態(tài)屬性引用的對象玉转。
  • 方法區(qū)中常量引用的對象。
  • 本地方法棧中引用的對象殴蹄。

再談引用

無論是通過引用計數(shù)算法判斷對象的引用數(shù)量究抓,還是通過可達性分析算法判斷對象的引用鏈?zhǔn)欠窨蛇_,判定對象是否存活都與引用有關(guān)饶套。

引用的四種類型:

  • 強引用:就是指在程序代碼之中普遍存在的漩蟆,類似Object obj = new Object() 這類引用,只要強引用還存在妓蛮,垃圾收集器永遠不會回收被引用的對象怠李。
  • 軟引用:是用來描述一個還有用但并非必須的對象。對于軟引用關(guān)聯(lián)著的對象蛤克,在系統(tǒng)將要發(fā)生內(nèi)存溢出異常之前捺癞,將會把這些對象列進回收范圍之中進行第二次回收。如果這次回收還沒有足夠的內(nèi)存构挤,才會拋出內(nèi)存溢出異常髓介。
  • 弱引用:也是用來描述非必須對象的,但是它的強度比軟引用更弱一些筋现,被弱引用關(guān)聯(lián)的對象只能生存到下一次垃圾發(fā)生之前唐础。當(dāng)垃圾收集器工作時箱歧,無論當(dāng)前內(nèi)存是否足夠,都會回收掉只被弱引用關(guān)聯(lián)的對象
  • 虛引用: 一個對象是否有虛引用的存在一膨,完全不會對其生存時間構(gòu)成影響呀邢,也無法通過虛引用來取得這個對象實例。為一個對象設(shè)置虛引用關(guān)聯(lián)的唯一目的就是能在這個對象被收集器回收時受到一個系統(tǒng)通知豹绪。

生存還是死亡

可達性分析法中不可達的對象也不是非死不可的价淌,而是處于緩刑階段。要宣告一個對象的死亡至少要經(jīng)過兩次標(biāo)記過程:

  • 1瞒津、當(dāng)經(jīng)過可達性分析后發(fā)現(xiàn)對象與GC Roots不可達蝉衣,那么它會被第一次標(biāo)記并且進行一次刷選,刷選的條件是此對象是否有必要執(zhí)行finalize方法巷蚪。
  • 2病毡、當(dāng)對象沒有覆蓋finalize方法或?qū)ο蟮膄inalize方法已經(jīng)被虛擬機執(zhí)行過。這兩種情況都會被視為不需要執(zhí)行finalizef方法钓辆。

如果這個對象有必要執(zhí)行finalize方法剪验,那么對象會被放在F-Queue的隊列中,并且會被由Java虛擬機自動創(chuàng)建的前联、低優(yōu)先級的Finalizer線程去執(zhí)行功戚。finalize方法是對象最后一次逃脫死亡的機會,在finalize方法后似嗤,GC將會對對象進行第二次標(biāo)記啸臀。如果對象在finalize方法中成功拯救自己,那么在第二次標(biāo)記時會被移出回收集合烁落,否則就真的被回收了乘粒。

回收方法區(qū)

很多人認(rèn)為方法區(qū)(虛擬機中的永久代)是沒有垃圾回收的,Java虛擬機規(guī)范也確實說過不要求虛擬機在方法區(qū)實現(xiàn)圾回收伤塌,因為方法區(qū)的垃圾收集效率很低灯萍。

方法區(qū)的垃圾收集主要回收兩部分內(nèi)容:廢棄常量無用的類

  • 回收廢棄常量:回收廢棄常量與回收J(rèn)ava堆中的對象類似每聪,以常量池中的字面量的回收為例:如果“abc”字符串存儲在常量池中旦棉,其他地方?jīng)]有任何對象引用常量池中的“abc”常量,那么進行垃圾回收時“abc”常量會被清理出常量池药薯。常量池中的其他類(接口)绑洛、方法、字段的符號引用也與此類似童本。
  • 無用的類 判斷無用的類比廢棄常量條件苛刻得多真屯。必須滿足以下三個條件:
    • 該類的所有實例都已被回收。
    • 加載該類的ClassLoader已被回收
    • 該類對應(yīng)的java.lang.Class對象沒有在任何地方被引用穷娱,無法在任何地方通過反射訪問該類绑蔫。

垃圾收集算法

標(biāo)記-清除算法

工作原理:算法主要分為兩個階段標(biāo)記和清除运沦,首先標(biāo)記出所有需要回收的對象,標(biāo)記完成后統(tǒng)一進行清除晾匠。

img

缺點:

  • 1茶袒、效率問題:標(biāo)記和清除兩個過程效率都不高
  • 2、空間問題:對象清除后會產(chǎn)生大量不連續(xù)的空間碎片凉馆,當(dāng)需要分配給大對象較大的內(nèi)存空間時會因為找不到足夠的連續(xù)空間而不得不提前出發(fā)下一次垃圾收集。

復(fù)制算法

為解決效率問題亡资,復(fù)制算法出現(xiàn)了:它將內(nèi)存空間分為大小相等的兩塊區(qū)域澜共,每次只使用其中一塊,當(dāng)進行垃圾收集時锥腻,將這塊區(qū)域中還存活的對象復(fù)制到另一塊嗦董,然后將這一塊內(nèi)存回收。這樣就不會產(chǎn)生內(nèi)存碎片的問題瘦黑。

img

缺點:這種算法實現(xiàn)簡單京革,運行高效,只是代價是每次只能使用內(nèi)存的一半幸斥,代價過高匹摇。

現(xiàn)在的商用虛擬機都采用這種收集算法回收新生代內(nèi)存。根IBM公司的研究表明甲葬,新生代中的內(nèi)存對象98%是朝生夕死的廊勃,所以不需要按照1:1的比例來劃分內(nèi)存空間,而是將內(nèi)存分為一塊較大的Eden區(qū)域经窖,兩塊較小的Survivor區(qū)域坡垫。每次只使用一塊Eden區(qū)域和一塊Survivor區(qū)域,當(dāng)進行垃圾收集時画侣,將Eden區(qū)域和Survivor區(qū)域仍然存活的對象復(fù)制到另一塊Survivor區(qū)域冰悠,然后將Eden區(qū)域和使用過的Survivor區(qū)域清除HotSpot虛擬機默認(rèn)的Eden和Survivor區(qū)域大小比例為8:1配乱,這樣只會浪費10%的內(nèi)存溉卓。

標(biāo)記-整理算法

復(fù)制算法在對象成活率較低的新生代比較適用,而對于對象成活率較高的老年代就需要進行較多的復(fù)制操作宪卿,效率明顯會減低的诵。所以針對老年代的特點,提出了標(biāo)記-整理算法:標(biāo)記清除過程仍然與標(biāo)記清楚算法一樣佑钾,只是在清除后將存活的對象都向一端移動西疤。

img

分代收集算法

當(dāng)前商業(yè)的虛擬機的垃圾收集算法都采用分代收集算法:根據(jù)對象存活周期的不同將內(nèi)存劃分為幾塊,一般把Java分為新生代和老年代休溶,在根據(jù)各個年代的特點選擇合適的收集算法代赁。在新生代中扰她,對象存活率低,適合使用復(fù)制算法芭碍,而老年代對象存活率較高徒役,適合使用標(biāo)記清除算法或標(biāo)記-整理算法。

參考資料

https://juejin.im/post/5aa0e8176fb9a028d663be1e

http://www.ityouknow.com/jvm/2017/08/29/GC-garbage-collection.html

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末窖壕,一起剝皮案震驚了整個濱河市忧勿,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌瞻讽,老刑警劉巖鸳吸,帶你破解...
    沈念sama閱讀 211,123評論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異速勇,居然都是意外死亡晌砾,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,031評論 2 384
  • 文/潘曉璐 我一進店門烦磁,熙熙樓的掌柜王于貴愁眉苦臉地迎上來养匈,“玉大人,你說我怎么就攤上這事都伪∨缓酰” “怎么了?”我有些...
    開封第一講書人閱讀 156,723評論 0 345
  • 文/不壞的土叔 我叫張陵院溺,是天一觀的道長楣嘁。 經(jīng)常有香客問我,道長珍逸,這世上最難降的妖魔是什么逐虚? 我笑而不...
    開封第一講書人閱讀 56,357評論 1 283
  • 正文 為了忘掉前任,我火速辦了婚禮谆膳,結(jié)果婚禮上叭爱,老公的妹妹穿的比我還像新娘。我一直安慰自己漱病,他們只是感情好买雾,可當(dāng)我...
    茶點故事閱讀 65,412評論 5 384
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著杨帽,像睡著了一般漓穿。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上注盈,一...
    開封第一講書人閱讀 49,760評論 1 289
  • 那天晃危,我揣著相機與錄音,去河邊找鬼。 笑死僚饭,一個胖子當(dāng)著我的面吹牛震叮,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播鳍鸵,決...
    沈念sama閱讀 38,904評論 3 405
  • 文/蒼蘭香墨 我猛地睜開眼苇瓣,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了偿乖?” 一聲冷哼從身側(cè)響起击罪,我...
    開封第一講書人閱讀 37,672評論 0 266
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎贪薪,沒想到半個月后外邓,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,118評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡古掏,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,456評論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了侦啸。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片槽唾。...
    茶點故事閱讀 38,599評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖光涂,靈堂內(nèi)的尸體忽然破棺而出庞萍,到底是詐尸還是另有隱情,我是刑警寧澤忘闻,帶...
    沈念sama閱讀 34,264評論 4 328
  • 正文 年R本政府宣布钝计,位于F島的核電站,受9級特大地震影響齐佳,放射性物質(zhì)發(fā)生泄漏私恬。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,857評論 3 312
  • 文/蒙蒙 一炼吴、第九天 我趴在偏房一處隱蔽的房頂上張望本鸣。 院中可真熱鬧,春花似錦硅蹦、人聲如沸荣德。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,731評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽涮瞻。三九已至,卻和暖如春假褪,著一層夾襖步出監(jiān)牢的瞬間署咽,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,956評論 1 264
  • 我被黑心中介騙來泰國打工嗜价, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留艇抠,地道東北人幕庐。 一個月前我還...
    沈念sama閱讀 46,286評論 2 360
  • 正文 我出身青樓,卻偏偏與公主長得像家淤,于是被迫代替她去往敵國和親异剥。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 43,465評論 2 348

推薦閱讀更多精彩內(nèi)容