<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>

          JVM-垃圾回收及內(nèi)存分配機制

          共 4648字,需瀏覽 10分鐘

           ·

          2021-03-10 21:53

          作為Javaer,經(jīng)常會見到或面試時被問到垃圾回收(GC)的相關內(nèi)容,那到底有哪些內(nèi)容需要關注呢?

          Java與C++之間有一堵由內(nèi)存動態(tài)分配和垃圾回收技術所圍成的圍墻,墻外的人想進去,墻里面的人卻想出來

          垃圾回收(Garbage Collection,GC):顧名思義,就是回收內(nèi)存垃圾所占用的空間,防止內(nèi)存泄漏,有效的使用內(nèi)存空間,對內(nèi)存中長時間未使用或者已經(jīng)死亡的對象進行整理和清除

          什么是垃圾?哪些內(nèi)存空間需要被回收

          在堆中存放著Java程序中幾乎所有的對象實例,對其進行垃圾回收的第一件事就是確定哪些對象需要“存活”,哪些對象已經(jīng)“死亡”

          判斷對象的“存亡”,常用的有兩種算法

          1、引用計數(shù)法

          在對象被創(chuàng)建時,JVM會給其分配一個引用計數(shù)器,當有一個地方引用時,計數(shù)器就加一;當不再引用時,計數(shù)器就減一。任何時刻引用計數(shù)器值為0的對象就是不可能再被使用的對象。引用計數(shù)法的原理簡單,判定效率也很高,但是在主流的Java虛擬機中并沒有選擇引用計數(shù)算法來管理內(nèi)存,主要原因,是因為引用計數(shù)算法無法解決對象之間相互引用的問題

          如下代碼,我們讓兩個實例對象相互引用,然后將其置空,通過查看GC日志,觀察兩個對象是否被當作垃圾回收,內(nèi)存占用是否有變

          如何查看GC日志

          啟動參數(shù)中設置  -XX:+PrintGCDetails

          public class ReferenceCountingGC {    public Object instance = null;    private static final int _1MB = 1024 * 1024;    /**     * 這個成員屬性的唯一意義就是占點內(nèi)存,以便能在GC日志中看清楚是否有回收過     */    private byte[] bigSize = new byte[5 * _1MB];

          public static void testGC(){ ReferenceCountingGC referenceA = new ReferenceCountingGC(); ReferenceCountingGC referenceB = new ReferenceCountingGC();
          referenceA.instance = referenceB; referenceB.instance = referenceA;
          referenceA = null; referenceB = null;
          System.gc(); }
          public static void main(String[] args) { testGC(); }
          }


          未啟用GC的內(nèi)存占用情況

          啟用GC后的內(nèi)存占用情況

          從上可知,System.gc()確實執(zhí)行了垃圾回收過程,內(nèi)存占用情況也從15444K->650K,兩個對象的大小不止650K,新生代肯定放不下,可以斷定,確實被回收掉了。這也證明了當前JVM使用的垃圾回收算法不是引用計數(shù)法


          2、可達性分析算法

          可達性分析算法的思路是從一系列被稱為“GC Roots”的根對象為起始節(jié)點,從這些節(jié)點開始根據(jù)引用關系向下搜索,搜索過程中所走的路徑成為“引用鏈”,如果某個對象到“GC Roots”之間沒有任何的引用鏈相連,或者用圖論的話來說從GC Roots到這個對象不可達時,則證明此對象是不可能再被使用的。

          如上圖,object 5、object 6、object 7雖然相互之間有關聯(lián),但是GC Roots不可達,所以他們就是要被回收的對象


          哪些對象被作為GC Roots呢(來自《深入立即Java虛擬機-JVM高級特性與最佳實踐(周志明版)》)

          (1)在虛擬機棧(棧楨中的本地變量表)中引用的對象,譬如各個線程被調(diào)用的方法堆棧中使用到的參數(shù),局部變量表、臨時變量等;

          (2)在方法區(qū)中類靜態(tài)屬性引用的對象,譬如Java類的引用類型靜態(tài)變量;

          (3)在方法區(qū)中常量引用的對象,譬如字符串常量池(String Table)里的引用;

          (4)在本地方法棧中JNI(Native方法)引用的對象;

          (5)Java虛擬機內(nèi)部的引用,如基本數(shù)據(jù)類型對應的Class對象,一些常駐的異常對象(比如NullPointException、OutOfMemoryError)等,還有系統(tǒng)類加載器

          (6)所有被同步鎖(synchronized關鍵字)持有的對象

          (7)反應Java虛擬機內(nèi)部情況的JMXBean、JVMTI中注冊的回調(diào)、本地代碼緩存等


          不管是引用計數(shù)算法還是可達性分析算法,判斷對象的存亡都與引用有關,而引用根據(jù)強度還可分為四種類型

          (1)強引用:常見的“Object object = new Object()”這種引用關系,就是強引用。無論任何情況下,只要強引用關系還在,該對象就不會被回收;

          (2)軟引用:當內(nèi)存不夠,即將發(fā)生內(nèi)存溢出時,該類對象將會被回收。可以使用SoftReference類來實現(xiàn)軟引用;

          (3)弱引用:無論內(nèi)存是否足夠,只有有進行垃圾回收,弱引用關聯(lián)的對象都會被回收。可以用WeakReference來實現(xiàn);

          (4)虛引用:虛引用并不會決定對象的生命周期。如果一個對象僅持有虛引用,那么它就和沒有任何引用一樣,在任何時候都可能被垃圾回收器回收。虛引用主要用來跟蹤對象被垃圾回收器回收的活動


          特殊情況

          可達性分析算法中判定不可達的對象不會立即被回收,這些對象需要經(jīng)歷兩次標記才能“死亡”

          可達性分析之后未與GC Roots關聯(lián)的對象將會被第一次標記;

          然后會根據(jù)是否有必要執(zhí)行finalize()方法去進行一次篩選;

          有必要執(zhí)行的將會放到一個隊列中,然后等待執(zhí)行finalize()方法

          finalize()中對象只要重新與GC Roots上的引用鏈上的對象建立關聯(lián),那么在第二次標記時,將會被移除“即將回收”集合,如果對象沒有被引用,將會被第二次標記,此時將會被正式宣告死亡,然后被回收掉

          注意:finalize()方法只會被系統(tǒng)調(diào)用執(zhí)行一次


          知道了哪些對象會被回收之后,接下來就是怎么回收這些垃圾了

          分代收集算法

          在Java虛擬機中,堆內(nèi)存一般分為兩個部分,新生代和老年代(比例為新生代:老年代=1:2),新生代又分為Eden區(qū)、S0區(qū)和S1區(qū)(Eden:S0:S1=8:1:1),如需詳細了解內(nèi)存結構可參考深入理解JVM-JVM內(nèi)存模型(二),這樣分代的原因是因為大多數(shù)對象都是“朝生夕死”的,在內(nèi)存中的存活時間并不長,在新生代中就已經(jīng)完成了其使命,然后部分對象經(jīng)過S0區(qū)與S1區(qū)之間交換,一部分被回收,一部分晉升到老年代。分代收集算法實際上是垃圾回收的基礎框架,以此框架,根據(jù)不同區(qū)域的對象特征,采用不同的收集算法來執(zhí)行GC過程


          標記-清除算法

          顧名思義,標記、清除是該算法的兩個步驟先標記所有需要被回收的對象,然后統(tǒng)一回收掉所有被標記的對象;或者標記所有不需要被回收的對象,然后統(tǒng)一回收掉所有未被標記的對象。標記過程其實就是對象是否屬于垃圾的過程

          主要缺點:

          (1)執(zhí)行效率不穩(wěn)定,如果堆中有大量對象,且其中大部分是需要被回收的,這樣就需要大量的標記和清除動作,其效率便會降低

          (2)內(nèi)存碎片化問題,標記清楚之后,內(nèi)存空間中會出現(xiàn)大量的不連續(xù)的內(nèi)存碎片,會導致需要為較大對象分配內(nèi)存空間時,沒有足夠大的連續(xù)空間分配,導致再一次的垃圾收集


          標記-復制算法

          該算法的原理是將內(nèi)存容量按照對象的特征分為大小相等的兩塊,每次只使用其中一塊。當一塊的內(nèi)存使用完之后,對這塊內(nèi)存進行標記處理,將存活對象移到另一塊內(nèi)存上,再將當前內(nèi)存塊上的被標記對象一次性清除掉;如果存活對象較多,會產(chǎn)生大量復制操作,此時則將被標記對象移到另一塊內(nèi)存,并進行清除

          優(yōu)點:高效、簡單

          缺點:存在一定的內(nèi)存浪費

          新生代中的S0區(qū)和S1區(qū)就是這樣的一種算法實現(xiàn)


          標記-整理算法

          該算法的原理和標記-清除算法的原理相似,但是其標記后的處理與標記-清除不同,標記-清除算法標記完成之后,直接將被標記的對象清除,標記-整理算法標記完成之后,是將存活對象向內(nèi)存空間一端移動,然后清理掉邊界以外的內(nèi)存

          如果移動存活對象,對于老年代這種大量對象存活區(qū)域,移動對象并更新所有引用這些對象的地方,必須全程暫停用戶應用程序,這種暫停也叫做“Stop The World”


          Java的自動內(nèi)存管理,本質(zhì)上是自動解決兩個問題:自動給對象分配內(nèi)存和自動回收分配給對象的內(nèi)存

          前面已經(jīng)說過內(nèi)存回收,下面說下內(nèi)存分配

          對象的內(nèi)存分配從概念上講,應該都是堆上分配(實際上也會有棧上分配,需要自行去尋找下答案,此篇不做解釋)

          1、對象優(yōu)先在Eden分配

          大多數(shù)情況下,對象在新生代Eden區(qū)分配,當Eden區(qū)沒有足夠空間進行分配時,虛擬機會發(fā)起一次Minor GC

          2、大對象直接進入老年代

          大對象指的是需要大量連續(xù)內(nèi)存空間的Java對象,大對象的復制操作將會占用高額的內(nèi)存復制開銷,應盡量避免,HotSpot虛擬機提供了 -XX:PretenureSizeThreshold參數(shù),指定大于該設置值的對象直接在老年代分配,這樣是為了避免在Eden區(qū)及兩個幸存者區(qū)之間來回復制,產(chǎn)生不必要的開銷

          3、長期存活的對象都將進入老年代

          對象在Eden區(qū)產(chǎn)生,在新生代每經(jīng)歷一次Minor GC,其年齡+1,當該對象的年齡計數(shù)器超過一定的閾值(默認15)時,其將進入老年代。閾值可通過參數(shù) +XX:MaxTenuringThreshold設置

          4、動態(tài)對象年齡判定

          HotSpot虛擬機中并不是永遠要求對象年齡達到閾值才能晉升老年代,如果在幸存者空間中低于或等于某個年齡的所有對象大小的總和大于幸存者空間的一半,年齡大于或等于該年齡的對象就可以直接進入老年代,不用等到閾值

          5、空間內(nèi)存分配擔保

          在發(fā)生Minor GC之前,虛擬機必須先檢查老年代最大可用的連續(xù)空間是否大于新生代所有對象總空間,如果滿足,則此次Minor GC可以確保是安全的;如果不成立,則虛擬機會先查看是否設置 -XX:HandlePromotionFailure參數(shù)設置的值是否允許擔保失敗;如果允許,則會繼續(xù)檢查老年代最大可用的連續(xù)空間是否大于歷次晉升到老年代對象的平均大小,如果大于,將會進行一次Minor GC,盡管是有風險的;如果小于,或者 -XX:HandlePromotionFailure設置不允許冒險,那這時就要改為進行一次Full GC


          以上內(nèi)容為部分知識總結,如需詳細了解,請參看《深入理解Java虛擬機-JVM高級特性與最佳實踐(周志明第三版)》


          小貼士:JVM相關內(nèi)容如需深入了解,可在B站觀看尚硅谷宋紅康老師的視頻,然后再搭配周志明老師的《深入理解Java虛擬機-JVM高級特性與最佳實踐(周志明第三版)》,視頻內(nèi)容是與書籍相同步的,兩者共同食用效果更佳


          !!!!來都來了,點個贊、在看、收藏、分享再走吧

          瀏覽 62
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          <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>
                  北条麻妃黄色视频免费播放 | 日韩欧美一级免费 | 久久国产精品精品国产色综合 | 成人精品天堂一区二区三区五区 | www.俺去啦 |