<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          到底誰才是垃圾?

          共 16645字,需瀏覽 34分鐘

           ·

          2021-07-13 19:16

          △Hollis, 一個(gè)對(duì)Coding有著獨(dú)特追求的人△
          這是Hollis的第 363 篇原創(chuàng)分享
          作者 l zyz1992
          來源 l Hollis(ID:hollischuang)
          作為 Java 程序員,我們是幸福的,因?yàn)槲覀儾恍枰芾硐到y(tǒng)中的垃圾。我們只需要將重點(diǎn)放在業(yè)務(wù)中就可以了。至于垃圾什么的就交給天生的垃圾收集器就可以了。
          那既然都這么說了,我們干嘛還要花心思來學(xué)習(xí)這些呢?我們學(xué)習(xí)這些肯定是為了更好的理解我們系統(tǒng)的底層運(yùn)行原理啊,這樣才能有針對(duì)性的寫出 “更適合” JVM的代碼。才能讓我們的代碼更健壯和安全,更關(guān)鍵的是能根據(jù) JVM 的特性有針對(duì)性的進(jìn)行調(diào)節(jié)和優(yōu)化,最終寫出執(zhí)行效率高的代碼。
          到底誰是垃圾?
          什么樣的對(duì)象可以稱為垃圾對(duì)象?換句話說:在垃圾收集器工作的時(shí)候,哪些對(duì)象是可以被回收的,哪些對(duì)象是不可以被回收的?判斷的標(biāo)準(zhǔn)是什么?系統(tǒng)中的對(duì)象千千萬,怎么才能準(zhǔn)確無誤的找出來并“殺”掉就顯得尤為重要。
          為了解決上面的問題。JVM 專門設(shè)計(jì)一套判斷對(duì)象是的是垃圾的算法——可達(dá)性分析
          可達(dá)性分析的原理是:根據(jù)每一個(gè)對(duì)象,一層一層的引用往上找,說白了就是看看那些地方在引用著這個(gè)對(duì)象。直到找到能被稱之為GC Roots的對(duì)象在引用這個(gè)這個(gè)對(duì)象,那么這個(gè)時(shí)候 JVM 就認(rèn)為這個(gè)對(duì)象是不是垃圾對(duì)象。
          也就是在垃圾回收的時(shí)候是不會(huì)去回收這部分對(duì)象的。反之,這樣的對(duì)象就可以被稱為垃圾對(duì)象。也就意味著是會(huì)被在垃圾收集器工作的時(shí)候就會(huì)回收這部分對(duì)象。



          GC Roots
          說到這里,哪些是垃圾對(duì)象我們是可以判斷了。那么剛剛提到的 GC Roots 又是什么鬼?簡(jiǎn)單的來講,靜態(tài)變量、局部變量、常量、本地方法棧中的對(duì)象都可以當(dāng)做GC Roots。但是一般最常見的就是:靜態(tài)變量、局部變量。
          我們姑且先這個(gè)記住,也就是凡是被這些對(duì)象引用的對(duì)象,就是不能被回收的。換言之,系統(tǒng)是在某些地方還在使用這些對(duì)象,這些對(duì)象我們也稱之為強(qiáng)引用。對(duì)應(yīng)的還有軟引用,弱引用和虛引用。
          • 強(qiáng)引用(使用頻率:☆☆☆☆☆)
              我們平時(shí)開發(fā)時(shí)候通過 new 關(guān)鍵創(chuàng)建出來的對(duì)象就是強(qiáng)引用,這類對(duì)象在垃圾回收的時(shí)候只要是能找到 G CRoots,那么他們是不會(huì)被回收的。
          • 軟引用(使用頻率:☆☆☆☆)
              所謂軟引用,就是表示該對(duì)象在垃圾回收期間,不軟是否被其他對(duì)象引用,只要是內(nèi)存空間不夠了,那么該對(duì)象就會(huì)別垃圾收集器回收。(PS:這個(gè)也是大家很容易和弱引用搞混淆的一個(gè)術(shù)語。我相信你平時(shí)開發(fā)常用的一定是 SoftReference ,而很少使用 WeakReference 。也就是說,強(qiáng)引用下面的一個(gè)就是軟引用。希望能幫助大家理解這兩個(gè)之間的區(qū)別。
          • 弱引用(使用頻率:☆)
              這類引用存在的價(jià)值更容易被忽視,只要是在垃圾回收階段,不管內(nèi)存是否足夠,該類型的對(duì)象都會(huì)被垃圾收集器回收。
          • 虛引用(使用頻率:程序員基本不會(huì)使用到)
              虛引用”顧名思義,就是形同虛設(shè),與其他幾種引用都不同,虛引用并不會(huì)決定對(duì)象的生命周期。如果一個(gè)對(duì)象僅持有虛引用,那么它就和沒有任何引用一樣,在任何時(shí)候都可能被垃圾回收。虛引用主要用來跟蹤對(duì)象被垃圾回收的活動(dòng)。虛引用與軟引用和弱引用的一個(gè)區(qū)別在于:虛引用必須和引用隊(duì)列(ReferenceQueue)聯(lián)合使用



          JVM 內(nèi)存結(jié)構(gòu)
          到此為止,我們已經(jīng)知道了哪些對(duì)象是垃圾已經(jīng)如何判斷垃圾對(duì)象了。接下來就是要回收了。但是在學(xué)習(xí)回收之前,我們還需要知道JVM內(nèi)存區(qū)域的劃分。換句話說就是回收對(duì)象是在哪里進(jìn)行的?我們先來看下 JVM 的內(nèi)存結(jié)構(gòu)(這種模型僅僅是人們?yōu)榱烁玫膶W(xué)習(xí)和理解 JVM 還虛擬出來的)

          以上結(jié)構(gòu)看起來并不復(fù)雜,主要由五大部分組成:方法區(qū)、堆內(nèi)存、虛擬機(jī)棧(棧)、本地方法棧(一般不關(guān)注)、程序計(jì)數(shù)器。其中方法區(qū)和堆內(nèi)存是線程共享的。其他三個(gè)是線程私有的。他們的主要作用如下:
          方法區(qū)
          被所有線程共享的區(qū)間,用來保存類信息、常量、靜態(tài)變量、也就是被虛擬機(jī)編譯后的代碼。換句話說:靜態(tài)變量、常量、類信息(版本信息、方法簽名、屬性等)和運(yùn)行時(shí)常量池存在方法區(qū)中。其中常量池是方法區(qū)的一部分。
          堆內(nèi)存(垃圾回收的重中之重)
          是 Java 虛擬機(jī)所管理的所有的內(nèi)存區(qū)域中最大的一塊內(nèi)存區(qū)域。堆內(nèi)存被所有線程的共享。
          主要存放使用 new 關(guān)鍵字創(chuàng)建的對(duì)象。所有對(duì)象實(shí)例以及數(shù)組都要在堆上分配。
          垃圾收集器就是根據(jù)GC算法,收集堆上對(duì)象所占用的內(nèi)存空間(這也是垃圾回收的重點(diǎn)核心區(qū)域)。
          虛擬機(jī)棧
          虛擬棧,是由一個(gè)一個(gè)的棧幀(棧幀:理解為方法的標(biāo)記即可),他是線程私有的,生命周期同線程一樣。
          每個(gè)方法在執(zhí)行的同時(shí)都會(huì)創(chuàng)建一個(gè)棧幀(Stack Frame)用于存儲(chǔ)局部變量表、操作數(shù)棧、動(dòng)態(tài)鏈接、方法出口等信息。
          局部變量表存放了編譯期可知的各種基本數(shù)據(jù)類型(8個(gè)基本數(shù)據(jù)類型)、對(duì)象引用(地址指針)、returnAddress類型。局部變量表所需的內(nèi)存空間在編譯期間完成分配。在運(yùn)行期間不會(huì)改變局部變量表的大小。
          這個(gè)區(qū)域規(guī)定了兩種異常狀態(tài):如果線程請(qǐng)求的棧深度大于虛擬機(jī)所允許的深度,則拋出StackOverflowError異常;如果虛擬機(jī)棧可以動(dòng)態(tài)擴(kuò)展,在擴(kuò)展時(shí)無法申請(qǐng)到足夠的內(nèi)存,就會(huì)拋出OutOfMemoryError異常。
          程序計(jì)數(shù)器
          程序計(jì)數(shù)器是線程私有的,里面記錄的就是即將要執(zhí)行的一條的CPU指令,因?yàn)樵诙嗑€程環(huán)境中,必然會(huì)存在線程之之間的切換,這樣JVM就需要有一套方案來記錄某個(gè)線程在之前執(zhí)行到了哪里。這就是程序計(jì)數(shù)器的作用
          本地方法棧
          記錄的就是本地的通過C/C++ 寫的一些程序(PS:這個(gè)空間中沒有規(guī)定 OOM,也就是不會(huì)發(fā)生OOM的情況。因?yàn)槌绦蛴?jì)數(shù)器存儲(chǔ)的是字節(jié)碼文件的行號(hào),而這個(gè)范圍是可知曉的,在一開始分配內(nèi)存時(shí)就可以分配一個(gè)絕對(duì)不會(huì)溢出的內(nèi)存)

          堆內(nèi)存的詳細(xì)結(jié)構(gòu)

          上面是說到了,堆內(nèi)存是垃圾回收的重中之重,但是這并不意味這對(duì)象就是很籠統(tǒng)的在堆內(nèi)存中的,他們也會(huì)被安排和分配到堆的不同的區(qū)域中。而堆內(nèi)存主要是這么劃分的,堆首先被劃分成兩大部分:年輕代(新生代)和老年代。年輕代又劃分為:Eden、From Survivor、To Survivor。
          其中年輕代和老年代所占的內(nèi)存口空間比例為:1:2。年輕代中的Eden、From Survivor、To Survivor 占比為:8:1:1。畫個(gè)圖來幫助大家更形象的理解下:

          大家不要急,一步一步來。



          垃圾回收算法
          從本小節(jié)開始,就是本文的重點(diǎn)了。JVM 在垃圾回收的時(shí)候:
          ① 到底使用了哪些垃圾回收算法?
          ② 分別在什么場(chǎng)景下使用?
          ③ 各自的優(yōu)缺點(diǎn)?
          下面就來正式的介紹下垃圾回收算法

          標(biāo)記-清除

          標(biāo)記清除是最簡(jiǎn)單和干脆的一種垃圾回收算法,他的執(zhí)行流程是這樣子的:當(dāng) JVM 標(biāo)記出內(nèi)存中的垃圾以后,直接將其清除,但是這樣有一個(gè)很明顯的缺點(diǎn),就是會(huì)導(dǎo)致內(nèi)存空間的不連續(xù),也就是會(huì)產(chǎn)生很多的內(nèi)存碎片。先畫個(gè)圖來看下

          我們使用上圖左邊的圖來表示垃圾回收之前的樣子,黑色的區(qū)域表示可以被回收的垃圾對(duì)象。這些對(duì)象在內(nèi)存空間中不是連續(xù)的。右側(cè)這張圖表示是垃圾回收過后的內(nèi)存的樣子。可以很明顯的看到里面纏身了斷斷續(xù)續(xù)的 內(nèi)存碎片。
          那說半天垃圾不是已經(jīng)被回收了嗎??jī)?nèi)存碎片就內(nèi)存碎片唄。又能咋地?
          好,我來這么告訴你,現(xiàn)在假設(shè)這些內(nèi)存碎片所占用的口空間之和是1 M,現(xiàn)在新創(chuàng)建了一個(gè)對(duì)象大小就是 1 M,但是很遺憾的是,此時(shí)內(nèi)存空間雖然加起來有 1 M,但是并不是連續(xù)的,所以也就無法存放這大對(duì)象。也就是說這樣勢(shì)必會(huì)造成內(nèi)存空間的浪費(fèi),這就是內(nèi)存碎片的危害。

          這么一說標(biāo)記-清除就沒有優(yōu)點(diǎn)了嗎??jī)?yōu)點(diǎn)還是有的:速度快
          到此,我們來對(duì)標(biāo)記-清除來做一個(gè)簡(jiǎn)單的優(yōu)缺點(diǎn)小結(jié):
          • 優(yōu)點(diǎn)
              速度快,因?yàn)椴恍枰苿?dòng)和復(fù)制對(duì)象
          • 缺點(diǎn)
              會(huì)產(chǎn)生內(nèi)存碎片,造成內(nèi)存的浪費(fèi)

          標(biāo)記-復(fù)制

          上面的清除算法真的太差勁了。都不管后來人能不能存放的下,就直接啥也不管的去清除對(duì)象。所以升級(jí)后就來了復(fù)制算法。
          復(fù)制算法的工作原理是這樣子的:首先將內(nèi)存劃分成兩個(gè)區(qū)域。新創(chuàng)建的對(duì)象都放在其中一塊內(nèi)存上面,當(dāng)快滿的時(shí)候,就將標(biāo)記出來的存活的對(duì)象復(fù)制到另一塊內(nèi)存區(qū)域中(注意:這些對(duì)象在在復(fù)制的時(shí)候其內(nèi)存空間上是嚴(yán)格排序且連續(xù)的),這樣就騰出來一那一半就又變成了空閑空間了。依次循環(huán)運(yùn)行。

          在回收前將存活的對(duì)象復(fù)制到另一邊去。然后再回收垃圾對(duì)象,回收完就類似下面的樣子:

          如果再來新對(duì)象被創(chuàng)建就會(huì)放在右邊那塊內(nèi)存中,當(dāng)內(nèi)存滿了,繼續(xù)將存活對(duì)象復(fù)制到左邊,然后清除掉垃圾對(duì)象。
          標(biāo)記-復(fù)制算法的明顯的缺點(diǎn)就是:浪費(fèi)了一半的內(nèi)存,但是優(yōu)點(diǎn)是不會(huì)產(chǎn)生內(nèi)存碎片。所以我們?cè)谧黾夹g(shù)的時(shí)候經(jīng)常會(huì)走向一個(gè)矛盾點(diǎn)地方,那就是:一個(gè)新的技術(shù)的引入,必然會(huì)帶來新的問題。
          到這里我們來簡(jiǎn)單小結(jié)下標(biāo)記-復(fù)制算法的優(yōu)缺點(diǎn):
          • 優(yōu)點(diǎn)
              內(nèi)存空間是連續(xù)的,不會(huì)產(chǎn)生內(nèi)存碎片
          • 缺點(diǎn)
              1、浪費(fèi)了一半的內(nèi)存空間
              2、復(fù)制對(duì)象會(huì)造成性能和時(shí)間上的消耗
          說到底,似乎這兩種垃圾回收回收算法都不是很好。而且在解決了原有的問題之后,所帶來的新的問題也是無法接受的。所以又有了下面的垃圾回收算法。

          標(biāo)記-整理

          標(biāo)記-整理算法是結(jié)合了上面兩者的特點(diǎn)進(jìn)行演化而來的。具體的原理和執(zhí)行流程是這樣子的:我們將其分為三個(gè)階段:
          第一階段為標(biāo)記
          第二階段為整理
          標(biāo)記:它的第一個(gè)階段與標(biāo)記-清除算法是一模一樣的,均是遍歷 GC Roots,然后將存活的對(duì)象標(biāo)記。
          整理:移動(dòng)所有存活的對(duì)象,且按照內(nèi)存地址次序依次排列,然后將末端內(nèi)存地址以后的內(nèi)存全部回收。因此,第二階段才稱為整理階段。
          我們是畫圖說話,下面這張圖是垃圾回收前的樣子。

          下圖圖表示的第一階段:標(biāo)記出存活對(duì)象和垃圾對(duì)象;并清除垃圾對(duì)象

          白色空間表示被清理后的垃圾。
          下面就開始進(jìn)行整理:

          可以看到,現(xiàn)在即沒有內(nèi)存碎片,也沒有浪費(fèi)內(nèi)存空間。
          但是這就完美了嗎?他在標(biāo)記和整理的時(shí)候會(huì)消耗大量的時(shí)間(微觀上)。但是在大廠那種高并發(fā)的場(chǎng)景下,這似乎有點(diǎn)差強(qiáng)人意。
          到此,我們將標(biāo)記-整理的優(yōu)缺點(diǎn)整理如下:
          • 優(yōu)點(diǎn)
              1、不會(huì)產(chǎn)生內(nèi)存碎片
              2、不會(huì)浪費(fèi)內(nèi)存空間
          • 缺點(diǎn)
              太耗時(shí)間(性能低)
          到此為止,我們已經(jīng)了知道了標(biāo)記-清除、標(biāo)記-復(fù)制、標(biāo)記-整理三大垃圾回收算法的優(yōu)缺點(diǎn)。
          單純的從時(shí)間長(zhǎng)短上面來看:標(biāo)記-清除 < 標(biāo)記-復(fù)制 < 標(biāo)記-整理
          單純從結(jié)果來看:標(biāo)記-整理 > 標(biāo)記-復(fù)制 >= 標(biāo)記-清除
          知道了垃圾回收算法,還有以下這些問題等著我們?nèi)シ治觯?/span>
          ① 垃圾收集器都有哪些呢?
          ② 年輕代和老年代又分別是哪些垃圾收集算法?
          ③ 不同的垃圾收集器對(duì)應(yīng)哪些垃圾回收算法?
          ④ 年輕代和老年代分別使用哪些垃圾收集器?
          帶著這些問題,讓我們繼續(xù)往下看。






          什么樣的垃圾會(huì)進(jìn)入到老年代
          我們現(xiàn)在已經(jīng)知道了什么是垃圾,那現(xiàn)在問題是:什么樣的垃圾會(huì)進(jìn)入到老年代?對(duì)象進(jìn)入老年代的條件有三個(gè),滿足一個(gè)就會(huì)進(jìn)入到老年代:
          • 1、躲過15次GC。每次垃圾回收后,存活的對(duì)象的年齡就會(huì)加1,累計(jì)加到15次(jdk8默認(rèn)的),也就是某個(gè)對(duì)象躲過了15次垃圾回收,那么JVM就認(rèn)為這個(gè)是經(jīng)常被使用的對(duì)象,就沒必要再帶著年輕代中了。具體的次數(shù)可以通過 -XX:MaxTenuringThreshold 來設(shè)置在躲過多少次垃圾收集后進(jìn)去老年代。
          • 2、動(dòng)態(tài)對(duì)象年齡判斷。規(guī)則:在某個(gè) Survivor 中,如果有一批對(duì)象的大小總是大于該 Survivor 的 50%,那么此時(shí)大于等于該批對(duì)象年齡的對(duì)象機(jī)會(huì)會(huì)直接到老年代中。
          • 3、大對(duì)象直接進(jìn)入老年代。-XX:PretenureSizeThreshold 來設(shè)置大對(duì)象的臨界值,大于該值的就被認(rèn)為是大對(duì)象,就會(huì)直接進(jìn)入老年代。
          針對(duì)上面的三點(diǎn)來逐一分析。

          躲過15次 GC

          這個(gè)沒啥好說的,最好理解,就是在執(zhí)行了15次GC后,對(duì)象依舊存活,那么就將其移動(dòng)到老年代中去,沒執(zhí)行一次垃圾回收,存活的對(duì)象的年齡就+1,具體的執(zhí)行次數(shù)可以通過:-XX:PretenureSizeThreshold參數(shù)來設(shè)置。

          動(dòng)態(tài)對(duì)象年齡判斷

          這就有點(diǎn)難理解了,不過一定會(huì)給你講清楚的

          再來看下這個(gè)規(guī)則:在某個(gè) Survivor 中,如果有一批對(duì)象的大小總是大于該 Survivor 的 50%,那么此時(shí)大于等于該批對(duì)象年齡的對(duì)象機(jī)會(huì)會(huì)直接到老年代中
          o(╥﹏╥)o 還是沒理解。。。我們畫圖來理解試試

          假設(shè)現(xiàn)在 To 里面的如圖兩個(gè)對(duì)象大小總和50 M,且都是3歲了,因?yàn)?To 是100 M,所以這個(gè)時(shí)候我們就說在某個(gè) Survivor 中,如果有一批對(duì)象的大小總是大于該Survivor 的 50%。這個(gè)時(shí)候大于等于該批對(duì)象年齡的對(duì)象機(jī)會(huì)會(huì)直接到老年代中
          再換換句話說就是:當(dāng)前放對(duì)象的Survivor區(qū)域里(其中一塊區(qū)域,放對(duì)象的那塊s區(qū)),一批對(duì)象的總大小大于這塊Survivor區(qū)域內(nèi)存大小的50%(-XX:TargetSurvivor 修可以指定),那么此時(shí)大于等于這批對(duì)象年齡最大值的對(duì)象,就可以直接進(jìn)入老年代了。
          例如Survivor區(qū)域里現(xiàn)在有一比對(duì)象,年齡1+年齡2+年齡n的多個(gè)年齡對(duì)象總和超過了的多個(gè)年齡對(duì)象總和超過了區(qū)域的50%,此時(shí)就會(huì)把年齡n(含)以上的對(duì)象都放入老年代)。這個(gè)規(guī)則其實(shí)是希望那些可能是長(zhǎng)期存活的對(duì)象,盡早進(jìn)入老年代。對(duì)象動(dòng)態(tài)年齡判斷機(jī)制一般是在 Minor GC 之后觸發(fā)的

          大對(duì)象直接進(jìn)入老年代

          這個(gè)就簡(jiǎn)單了,-XX:PretenureSizeThreshold 來設(shè)置大對(duì)象的臨界值。如 -XX:PretenureSizeThreshold=1024 * 1024。即對(duì)象超過1M直接進(jìn)入老年代。其實(shí)大對(duì)象直接進(jìn)入到老年代還包含這種情況:那就是當(dāng) Eden 中執(zhí)行了 Minor GC 后,存活的對(duì)象的大小是 超過了100M了(上圖 from 和 to 都是100M)此時(shí)這些存活的對(duì)象也是直接進(jìn)入到老年代。
          說了半天對(duì)象都跑到老年代去了,那既然老年代這個(gè)牛逼,干嘛還分年輕代和老年代?年輕人,你不要急。后文我會(huì)全部道來。我們下面先來看看老年代空間如果不夠用怎么辦?



          老年代空間分配擔(dān)保
          上面說到了,對(duì)象在哪些情況下會(huì)進(jìn)入到老年代,年輕代倒是省心了,你不夠了就放到老年代,那如果老年代也不夠了呢?那又是如何處理呢?
          實(shí)際上是這樣的。在年輕代執(zhí)行 Minor GC 之前首先會(huì)檢查老年代的可用空間的大小是否是大于新生代所有對(duì)象的大小。為什么是所有對(duì)象,不應(yīng)該是存活的對(duì)象嗎?

          你想啊,假如年輕代經(jīng)過一次 Minor GC 后所有的對(duì)象都是存活的,這是不是就尷尬了(PS:所以這還是我們需要考慮的“臨界情況”。不要覺得一般情況或者是泛泛的說法,程序的嚴(yán)謹(jǐn)性就是在臨界情況下體現(xiàn)出來的)
          現(xiàn)在假設(shè)在 Minor GC之前,檢查發(fā)現(xiàn)老年代空間還真不夠了,那么首先會(huì)去檢查-XX:HandlerPromotionFailure的參數(shù)是否設(shè)置了,這個(gè)參數(shù)表示:是否設(shè)置空間分配擔(dān)保
          • 是:就會(huì)判斷老年代的剩余的空間的大小是否是大于之前的每一次 MinorGC 后進(jìn)入老年代的對(duì)象的平均的大小


          • 否:那么此時(shí)就會(huì)進(jìn)行FULL GC來為老年代騰出一些空間

          假設(shè)現(xiàn)在開啟了空間分配擔(dān)保,并且發(fā)現(xiàn)之前的每次 Minor GC 后的對(duì)象的平均大小(假設(shè)是10 M)是小于老年代可用空間的大小(假設(shè)現(xiàn)在是12 M)的,那么就會(huì)認(rèn)為本次 Minor GC 后差不多也是10 M的對(duì)象進(jìn)入到老年代。但是如果最終垃圾回收剩余存活對(duì)象大于13 M,那么就直接 OOM;

          如果沒有開啟空間分配擔(dān)保機(jī)制,那么就會(huì)觸發(fā)一次 Full GC(老年代的垃圾回收稱之為 Full GC),這樣看看能不能在老年代中在騰出一些空間,因?yàn)橐话憷夏甏械膶?duì)象是長(zhǎng)時(shí)間存活的,所以這招可能作用不是很大。
          假設(shè)Full GC 結(jié)束了,再嘗試進(jìn)行 Minor GC ,此時(shí)又可能有好幾種情況:
          第一種情況:Minor GC 后,剩余的存活對(duì)象的大小是小于 from 區(qū)大小的,那么對(duì)象直接進(jìn)入 from 區(qū)即可;
          第二種情況:Minor GC 后,剩余的存活對(duì)象的大小是大于 from 區(qū)大小的,但是是小于老年區(qū)可用空間大小的,那么對(duì)象直接進(jìn)入老年代;
          第三種情況:Minor GC 后,剩余的存活的對(duì)象的大小是大于 from 區(qū)大小的,同時(shí)也大于老年區(qū)可用的空間的大小,這個(gè)時(shí)候就會(huì)根據(jù)XX:HandlerPromotionFailure的設(shè)置來觸發(fā)一次 Full GC,如果此時(shí) Full GC后老年代的空間還是不夠存放 Minor GC 后剩下的對(duì)象。那么就 OOM。
          上面說了這么多我們來畫個(gè)圖整理和理解下,以年輕代快滿了為出發(fā)點(diǎn)(Minor GC前):




          年輕代的垃圾回收算法
          我們先來回頭看下這張圖(為了方便閱讀,我直接復(fù)制下來)

          對(duì)象在剛創(chuàng)建的時(shí)候(排除直接進(jìn)入到老年代的情況)。我們認(rèn)為都是被分配到年輕代的 Eden 中的,當(dāng) Eden 快滿的時(shí)候,就會(huì)觸發(fā)一次垃圾回收,稱之為:Minor GC(一般情況下 Eden 中經(jīng)過一次垃圾回收后存活的對(duì)象非常少,這就好像是一次請(qǐng)求創(chuàng)建了很多的臨時(shí)變量和對(duì)象,請(qǐng)求結(jié)束這些基本就全是垃圾了,這就是為什么 from 和 to 比例這么小的原因)
          將存活的對(duì)象移動(dòng)到 from 區(qū)域,此時(shí)存活的對(duì)象的年齡就 +1 ,并且將 from 和 to 的指向交換位置。首先來看下剛剛回收完垃圾將對(duì)象轉(zhuǎn)移到 from 的圖

          然后我們強(qiáng)調(diào)了一個(gè)詞,將 from 和 to 的指向交換位置:

          這樣子其實(shí)就是下面的樣子:

          (PS:真正的內(nèi)存空間的位置并沒有變化,實(shí)際變化的是from 和 to 的指向,這樣下次執(zhí)行 Minor GC 的時(shí)候還是將存回的對(duì)象放在 from 區(qū)域,你懂了沒?)
          然后 Eden 區(qū)域繼續(xù)存放新對(duì)象,當(dāng) Eden 再次快滿的時(shí)候,又會(huì)技術(shù)出發(fā) Yong GC(Minor GC 的另一個(gè)名字,為了讓大家了解的更全面,故意都使用下),此時(shí)垃圾回收的是 Eden 和 to 區(qū)域中的垃圾,因?yàn)樯弦淮未婊盍说膶?duì)象到這一次不一定就存活了。然后將他們存活的對(duì)象在移動(dòng)到 from 區(qū)域。然后交換 from 和 to 的位置指向。以此循環(huán)往復(fù)。



          垃圾收集器
          關(guān)于垃圾收集器其實(shí)現(xiàn)在更多關(guān)注的是 G1垃圾收集器,但是本文不會(huì)去介紹,這個(gè)會(huì)放在單獨(dú)的一篇文章去介紹的。目前常見的垃圾收集器有:
          ①Serial 垃圾收集器
          ②Serial Old 垃圾收集器
          ③ParNew 垃圾收集器
          ④CMS 垃圾收集器
          ⑤Parallel Scavenge 垃圾收集器
          ⑥Parallel Old 垃圾收集器
          ⑦G1 垃圾收集器
          他們具體工作在年輕代還是老年代我們來通過一張圖說明:

          箭頭表示年輕代是 xxx 老年代可以是 xxx,表示一種對(duì)應(yīng)關(guān)系。
          通過java -XX:+PrintCommandLineFlags -version命令可以查看當(dāng)前 JVM 使用的垃圾收集器

          新生代的垃圾收集器

          Serial(Serial/Serial Copying)
          最古老,最穩(wěn)定,簡(jiǎn)單高效,GC時(shí)候需要暫停用戶線程,限定單核CPU環(huán)境,是Client模式下的默認(rèn)新生代收集器(基本不再使用)。
          對(duì)應(yīng)的 JVM 參數(shù)為 -XX:UseSerialGC;開啟后,會(huì)使用Serial(Young區(qū)使用)+Serial Old(Old區(qū)使用)組合收集器。新生代、老年代都會(huì)使用串行回收收集器,新生代使用【標(biāo)記-復(fù)制算法】老年代使用【標(biāo)記-整理算法】
          ParNew(Parallel New Generation)
          新生代是并行老年代是串行。
          ParNew其實(shí)就是【Serial收集器新生代】的【并行多線程】版本。它是很多java虛擬機(jī)在運(yùn)行Server模式下的新生代的默認(rèn)的垃圾收集器。
          最常見的場(chǎng)景的是配合老年代的CMS GC工作,其余的行為和Serial收集器一樣 ParNew工作的時(shí)候同樣需要暫停其他的所有的線程 對(duì)應(yīng)的JVM參數(shù)為 -XX:UserParNewGC 啟用ParNew收集器,只作用于新生代,不影響老年代 開啟后,會(huì)使用ParNew(Young區(qū)使用)+Serial Old的收集器組合,新生代使用【復(fù)制算法】老年代 使用【標(biāo)記-整理算法】
          并行回收GC(Parallel/ParallelScavenge)(默認(rèn)收集器)
          新生代和老年代都是并行。
          Parallel Scavenge 收集器類似ParNew也是一個(gè)新生代的垃圾收集器,使用的【復(fù)制算法】,是一個(gè)并行多線程的垃圾收集器。俗稱吞吐量?jī)?yōu)先的收集器。一句話:串行收集器在新生代和老年的并行化。
          • 可控的吞吐量(運(yùn)行用戶的的代碼時(shí)間/(運(yùn)行用戶的代碼時(shí)間+垃圾收集時(shí)間))。也即運(yùn)行100分 鐘,垃圾收集時(shí)間為1分鐘,那么吞吐量就是99%。高吞吐量意味著高效的CPU利用率
          • 自適應(yīng)調(diào)節(jié)策略也是Parallel Scavenge 和 ParNew 的一個(gè)重要的區(qū)別(虛擬機(jī)會(huì)根據(jù)當(dāng)前的 系統(tǒng)的運(yùn)行情況手機(jī)性能監(jiān)控信息,動(dòng)態(tài)的調(diào)整這些參數(shù)以提供最合適的停頓時(shí)間(- XX:MaxGCPauseMillis)或最大的吞吐量)
          • 如果新生區(qū)激活-XX:+UseParallelGC(或者是-XX:UseParallelOldGC他們可以互相激活)老 年區(qū)就自動(dòng)使用Parallel Old,使用Parallel Scavenge收集器 - -XX:ParallelGCThreads=N 表示啟動(dòng)多少個(gè)線程 cpu>8 N=5/8 cpu<8 N=實(shí)際個(gè)數(shù)

          老年代的垃圾收集器

          串行GC(Serial Old/Serial MSC)
          運(yùn)行在Client模式(基本不再使用)
          并行GC(Parallel Old/Parallel MSC)
          是Parallel Scavenage的老年大版本,采用【標(biāo)記-整理】算法;
          并發(fā)標(biāo)記清除GC(CMS)
          是一種以獲取最短回收停頓時(shí)間為目標(biāo)的收集器。適用于重視服務(wù)器的相應(yīng)速度,希望系統(tǒng)的停頓時(shí)間最短。
          GC線程和用戶線程一起執(zhí)行(Serial Old將作為CMS出錯(cuò)的后備收集器),主要有4步過程(重要) - 初始標(biāo)記 - 并發(fā)標(biāo)記(和用戶線程一起工作) - 重新標(biāo)記 - 并發(fā)清除(和用戶線程一起工作)
          優(yōu)點(diǎn)他的優(yōu)點(diǎn)是并發(fā)收集低停頓、缺點(diǎn)是并發(fā)執(zhí)行,對(duì)CPU壓力大采用的【標(biāo)記-清除】算法會(huì)導(dǎo)致大量的碎片

          目前最常見的組合是:ParNew + CMS 垃圾收集器。鑒于篇幅限制和為了大家更好的消化本文內(nèi)容。更多關(guān)于 ParNew 和 CMS 的工作原理將放在下一篇文章分析。

          本文小結(jié)
          本文從【垃圾】到【垃圾回收算法】再到【垃圾收集器】,詳細(xì)的介紹了 JVM 中最最基本的一些概念和原理。本文重點(diǎn)關(guān)注點(diǎn)是原理和流程,所以內(nèi)容稍多,另外 JVM 的博大精深并非一朝一夕更不可能是一篇文章就能學(xué)會(huì)的。
          但是本文希望能給大家在 JVM 在學(xué)習(xí)之路上添磚加瓦。
           
          技術(shù)交流群

          最近有很多人問,有沒有讀者交流群,想知道怎么加入。

          最近我創(chuàng)建了一些群,大家可以加入。交流群都是免費(fèi)的,只需要大家加入之后不要隨便發(fā)廣告,多多交流技術(shù)就好了。

          目前創(chuàng)建了多個(gè)交流群,全國(guó)交流群、北上廣杭深等各地區(qū)交流群、面試交流群、資源共享群等。

          有興趣入群的同學(xué),可長(zhǎng)按掃描下方二維碼,一定要備注:全國(guó) Or 城市 Or 面試 Or 資源,根據(jù)格式備注,可更快被通過且邀請(qǐng)進(jìn)群。

          ▲長(zhǎng)按掃描


               

          往期推薦

          字節(jié)跳動(dòng)將取消大小周,加班要打申請(qǐng)!此前1/3員工不同意,有人擔(dān)心“一年少賺近10萬”


          谷歌:. apk 成為歷史!


          在線求CR,你覺得我這段Java代碼還有優(yōu)化的空間嗎?



          如果你喜歡本文,
          請(qǐng)長(zhǎng)按二維碼,關(guān)注 Hollis.
          轉(zhuǎn)發(fā)至朋友圈,是對(duì)我最大的支持。

          點(diǎn)個(gè) 在看 
          喜歡是一種感覺
          在看是一種支持
          ↘↘↘
          瀏覽 42
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  岛国爱情动作片,91,麻豆 | 色老太在线视频 | 一级无码在线 | 中国黄色一级毛骗 | 天天夜夜拍拍 |