<output id="qn6qe"></output>

    1. <output id="qn6qe"><tt id="qn6qe"></tt></output>
    2. <strike id="qn6qe"></strike>

      亚洲 日本 欧洲 欧美 视频,日韩中文字幕有码av,一本一道av中文字幕无码,国产线播放免费人成视频播放,人妻少妇偷人无码视频,日夜啪啪一区二区三区,国产尤物精品自在拍视频首页,久热这里只有精品12

      JVM面試篇(下)

      垃圾收集器

      簡述 Java 垃圾回收機(jī)制


      java 中,程序員是不需要顯示的去釋放一個(gè)對象的內(nèi)存的,而是由虛擬機(jī)自行執(zhí)行。在 JVM

      中,有一個(gè)垃圾回收線程,它是低優(yōu)先級的,在正常情況下是不會執(zhí)行的,只有在虛擬機(jī)空閑或

      者當(dāng)前堆內(nèi)存不足時(shí),才會觸發(fā)執(zhí)行,掃面那些沒有被任何引用的對象,并將它們添加到要回收

      的集合中,進(jìn)行回收。


      GC 是什么?為什么要 GC


      1. GC 是垃圾收集的意思(Gabage Collection),內(nèi)存處理是編程人員容易出現(xiàn)問題的地方,

        忘記或者錯(cuò)誤的內(nèi)存

      2. 回收會導(dǎo)致程序或系統(tǒng)的不穩(wěn)定甚至崩潰 Java 提供的 GC 功能可以自動監(jiān)測對象是否超過

        作用域從而達(dá)到自動

      3. 回收內(nèi)存的目的, Java語言沒有提供釋放已分配內(nèi)存的顯示操作方法。


      垃圾回收的優(yōu)點(diǎn)和原理。2 種回收機(jī)制


      Java 語言最顯著的特點(diǎn)就是引入了垃圾回收機(jī)制,它使 Java 程序員在編寫程序時(shí)不再考慮內(nèi)存

      管理的問題。

      由于有這個(gè)垃圾回收機(jī)制, Java 中的對象不再有“作用域”的概念,只有引用的對象才有“作

      用域”。


      1. 垃圾回收機(jī)制有效的防止了內(nèi)存泄露,可以有效的使用可使用的內(nèi)存。

      2. 垃圾回收器通常作為一個(gè)單獨(dú)的低級別的線程運(yùn)行,在不可預(yù)知的情況下對內(nèi)存堆中已經(jīng)死

        亡的或很長時(shí)間沒有用過的對象進(jìn)行清除和回收。


      程序員不能實(shí)時(shí)的對某個(gè)對象或所有對象調(diào)用垃圾回收器進(jìn)行垃圾回收。垃圾回收有分代復(fù)制

      垃圾回收標(biāo)記垃圾回收、增量垃圾回收


      垃圾回收器的基本原理是什么?


      對于 GC 來說,當(dāng)程序員創(chuàng)建對象時(shí),GC 就開始監(jiān)控這個(gè)對象的地址、大小以及使用情況。


      主動通知虛擬機(jī)進(jìn)行垃圾回收的辦法?


      通常,GC 采用有向圖的方式記錄管理堆(heap)中的所有對象。通過這種方式確定哪些對象

      是"可達(dá)的",哪些對象是"不可達(dá)的"。當(dāng) GC 確定一些對象為" 不可達(dá)"時(shí),GC 就有責(zé)任回收這

      些內(nèi)存間。


      垃圾回收器可以馬上回收內(nèi)存嗎?


      可以。程序員可以手動執(zhí)行 System.gc(),通知 GC 運(yùn)行,但是 Java 語言規(guī)范并不保證 GC 一

      定會執(zhí)行。


      我們能保證 GC 執(zhí)行嗎?


      不能,雖然你可以調(diào)用 System.gc() 或者 Runtime.gc(),但是沒有辦法保證GC 的執(zhí)行。


      Java 中引用類型有哪些?


      • 強(qiáng)引用:發(fā)生 gc 的時(shí)候不會被回收。

      • 軟引用:有用但不是必須的對象,在發(fā)生內(nèi)存溢出之前會被回收。

      • 弱引用:有用但不是必須的對象,在下一次 GC 時(shí)會被回收。

      • 虛引用(幽靈引用/幻影引用):無法通過虛引用獲得對象,用PhantomReference 實(shí)現(xiàn)虛

        引用,虛引用的用途是在 gc 時(shí)返回一個(gè)通知。


      強(qiáng)引用、軟引用、弱引用、虛引用的區(qū)別?


      思路: 先說一下四種引用的定義,可以結(jié)合代碼講一下,也可以擴(kuò)展談到ThreadLocalMap

      里弱引用用處。

      • 強(qiáng)引用

        我們平時(shí) new 了一個(gè)對象就是強(qiáng)引用,例如 Object obj = new Object();即使在內(nèi)存不足

        的情況下,JVM 寧愿拋出 OutOfMemory 錯(cuò)誤也不會回收這種對象

      • 軟引用

        如果一個(gè)對象只具有軟引用,則內(nèi)存空間足夠,垃圾回收器就不會回收它;如果內(nèi)存空間不

        足了,就會回收這些對象的內(nèi)存。

          SoftReference<String> softRef=new SoftReference<String>(str); // 軟引用
        
        • 用處:軟引用在實(shí)際中有重要的應(yīng)用,例如瀏覽器的后退按鈕。按后退時(shí),這個(gè)后退時(shí)

          顯示的網(wǎng)頁內(nèi)容是重新進(jìn)行請求還是從緩存中取出呢?這就要看具體的實(shí)現(xiàn)策略了。

          1. 如果一個(gè)網(wǎng)頁在瀏覽結(jié)束時(shí)就進(jìn)行內(nèi)容的回收,則按后退查看前面瀏覽過的頁面

            時(shí),需要重新構(gòu)建

          2. 如果將瀏覽過的網(wǎng)頁存儲到內(nèi)存中會造成內(nèi)存的大量浪費(fèi),甚至?xí)斐蓛?nèi)存溢出.

          如下代碼


      Browser prev = new Browser(); // 獲取頁面進(jìn)行瀏覽
      SoftReference sr = new SoftReference(prev); // 瀏覽完畢后置為軟引用 
      if(sr.get()!=null){
      	rev = (Browser) sr.get(); // 還沒有被回收器回收,直接獲取
      }else{
      	prev = new Browser(); // 由于內(nèi)存吃緊,所以對軟引用的對象回收了
      	sr = new SoftReference(prev); // 重新構(gòu)建
      }
      

      • 弱引用

        具有弱引用的對象擁有更短暫的生命周期。在垃圾回收器線程掃描它所管轄的內(nèi)存區(qū)域的過

        程中,一旦發(fā)現(xiàn)了只具有弱引用的對象,不管當(dāng)前內(nèi)存空間足夠與否,都會回收它的內(nèi)存。

          String str=new String("abc"); 
          WeakReference<String> abcWeakRef = newWeakReference<String>(str);
          str=null;
          等價(jià)于
          str = null;
          System.gc();
        
      • 虛引用

        如果一個(gè)對象僅持有虛引用,那么它就和沒有任何引用一樣,在任何時(shí)候都可能被垃圾回收

        器回收。虛引用主要用來跟蹤對象被垃圾回收器回收的活動。


      怎么判斷對象是否可以被回收?


      垃圾收集器在做垃圾回收的時(shí)候,首先需要判定的就是哪些內(nèi)存是需要被回收的,哪些對象是

      「存活」的,是不可以被回收的;哪些對象已經(jīng)「死掉」了,需要被回收。

      一般有兩種方法來判斷:

      • 引用計(jì)數(shù)器法:為每個(gè)對象創(chuàng)建一個(gè)引用計(jì)數(shù),有對象引用時(shí)計(jì)數(shù)器 +1,引用被釋放時(shí)計(jì)

        數(shù) -1,當(dāng)計(jì)數(shù)器為 0 時(shí)就可以被回收。它有一個(gè)缺點(diǎn)不能解決循環(huán)引用的問題;

      • 可達(dá)性分析算法:從 GC Roots 開始向下搜索,搜索所走過的路徑稱為引用鏈。當(dāng)一個(gè)對象

        到 GC Roots 沒有任何引用鏈相連時(shí),則證明此對象是可以被回收的。



      在 Java 中,對象什么時(shí)候可以被垃圾回收


      當(dāng)對象對當(dāng)前使用這個(gè)對象的應(yīng)用程序變得不可觸及的時(shí)候,這個(gè)對象就可以被回收了。

      垃圾回收不會發(fā)生在永久代,如果永久代滿了或者是超過了臨界值,會觸發(fā)完全垃圾回收(Full

      GC)。如果你仔細(xì)查看垃圾收集器的輸出信息,就會發(fā)現(xiàn)永久代也是被回收的。這就是為什么

      正確的永久代大小對避免 Full GC 是非常重要的原因.


      JVM 運(yùn)行時(shí)堆內(nèi)存如何分代?


      Java 堆從 GC 的角度還可以細(xì)分為: 新生代(Eden 區(qū)、 From Survivor 區(qū)和 To Survivor 區(qū))和

      老年代。

      參考圖 1:


      image


      參考圖 2:


      image


      1. 從圖中可以看出: 堆大小 = 新生代 + 老年代。其中,堆的大小可以通過參數(shù) –Xms、-Xmx 來

        指定。

      2. 默認(rèn)的,新生代 ( Young ) 與老年代 ( Old ) 的比例的值為 1:2 ( 該值可以通過參數(shù) –

        XX:NewRatio 來指定 ),

      即:新生代 ( Young ) = 1/3 的堆空間大小。老年代 ( Old ) = 2/3 的堆空間大小

      其中,新生代 ( Young ) 被細(xì)分為 Eden 和 兩個(gè) Survivor 區(qū)域,這兩個(gè)Survivor 區(qū)域分別被命

      名為 from 和 to,以示區(qū)分.

      默認(rèn)的,Eden: from : to = 8 :1 : 1 ( 可以通過參數(shù)–XX:SurvivorRatio 來設(shè)定 ),即: Eden

      = 8/10 的新生代空間大小,from = to = 1/10 的新生代空間大小


      JVM 每次只會使用 Eden 和其中的一塊 Survivor 區(qū)域來為對象服務(wù),所以無論什么時(shí)候,總是

      有一塊 Survivor 區(qū)域是空閑著的。

      因此,新生代實(shí)際可用的內(nèi)存空間為 9/10 ( 即 90% )的新生代空間。

      新生代

      是用來存放新生的對象。一般占據(jù)堆的 1/3 空間。由于頻繁創(chuàng)建對象,所以新生代會頻繁觸發(fā)

      MinorGC 進(jìn)行垃圾回收。新生代又分為 Eden 區(qū)、ServivorFrom、 ServivorTo 三個(gè)區(qū)。


      Eden 區(qū)

      Java 新對象的出生地(如果新創(chuàng)建的對象占用內(nèi)存很大,則直接分配到老年代)。當(dāng) Eden 區(qū)內(nèi)

      存不夠的時(shí)候就會觸發(fā) MinorGC,對新生代區(qū)進(jìn)行一次垃圾回收。


      Servivor from 區(qū)

      上一次 GC 的幸存者,作為這一次 GC 的被掃描者。


      Servivor to 區(qū)

      保留了一次 MinorGC 過程中的幸存者。


      MinorGC 的過程(復(fù)制->清空->互換)

      MinorGC 采用復(fù)制算法。

      1. eden、 servicorFrom 復(fù)制到 ServicorTo,年齡+1首先,把 Eden 和 ServivorFrom 區(qū)域

        中存活的對象復(fù)制到 ServicorTo 區(qū)域(如果有對象的年齡以及達(dá)到了老年的標(biāo)準(zhǔn),則賦值

        到老年代區(qū)),同時(shí)把這些對象的年齡+1(如果 ServicorTo 不夠位置了就放到老年區(qū));

      2. 清空 eden、 servicorFrom然后,清空 Eden 和 ServicorFrom 中的對象;

      3. ServicorTo 和 ServicorFrom 互換最后, ServicorTo 和 ServicorFrom 互換,原

        ServicorTo 成為下一次 GC 時(shí)的 ServicorFrom 區(qū)。


      老年代

      主要存放應(yīng)用程序中生命周期長的內(nèi)存對象。


      老年代的對象比較穩(wěn)定,所以 MajorGC (常常稱之為 FULL GC)不會頻繁執(zhí)行。在進(jìn)行 FULL

      GC 前一般都先進(jìn)行了一次 MinorGC,使得有新生代的對象晉身入老年代,導(dǎo)致空間不夠用時(shí)才

      觸發(fā)。當(dāng)無法找到足夠大的連續(xù)空間分配給新創(chuàng)建的較大對象時(shí)也會提前觸發(fā)一次 MajorGC 進(jìn)

      行垃圾回收騰出空間。


      FULL GC 采用標(biāo)記清除算法:首先掃描一次所有老年代,標(biāo)記出存活的對象,然后回收沒有標(biāo)記

      的對象。ajorGC 的耗時(shí)比較長,因?yàn)橐獟呙柙倩厥铡ULLGC 會產(chǎn)生內(nèi)存碎片,為了減少內(nèi)存

      損耗,我們一般需要進(jìn)行合并或者標(biāo)記出來方便下次直接分配。當(dāng)老年代也滿了裝不下的時(shí)候,

      就會拋出 OOM(Outof Memory)異常.


      永久代

      指內(nèi)存的永久保存區(qū)域,主要存放 Class 和 Meta(元數(shù)據(jù))的信息,Class 在被加載的時(shí)候被放

      入永久區(qū)域, 它和和存放實(shí)例的區(qū)域不同,GC 不會在主程序運(yùn)行期對永久區(qū)域進(jìn)行清理。所以這

      也導(dǎo)致了永久代的區(qū)域會隨著加載的Class 的增多而脹滿,最終拋出 OOM 異常。


      JVM 內(nèi)存為什么要分成新生代,老年代,持久代。新生代中為什么要分為 Eden和 Survivor。


      思路: 先講一下 JAVA 堆,新生代的劃分,再談?wù)勊鼈冎g的轉(zhuǎn)化,相互之間一些參數(shù)的配置

      (如: –XX:NewRatio,–XX:SurvivorRatio 等),再解釋為什么要這樣劃分,最好加一點(diǎn)自己

      的理解。


      答:

      這樣劃分的目的是為了使 JVM 能夠更好的管理堆內(nèi)存中的對象,包括內(nèi)存的分配以及回收。

      • 共享內(nèi)存區(qū)劃分

          -  共享內(nèi)存區(qū) = 持久帶 + 堆
             持久帶 = 方法區(qū) + 其他
             Java 堆 = 老年代 + 新生代
             新生代 = Eden + S0 + S1
        
      • 一些參數(shù)的配置

          默認(rèn)的,新生代 ( Young ) 與老年代 ( Old ) 的比例的值為 1:2 ,可以通過參數(shù)
          
          –XX:NewRatio 配置。
          
          默認(rèn)的,Eden : from : to = 8 : 1 : 1 ( 可以通過參數(shù) –XX:SurvivorRatio 來設(shè)定)
          
          Survivor 區(qū)中的對象被復(fù)制次數(shù)為 15(對應(yīng)虛擬機(jī)參數(shù)-XX:+MaxTenuringThreshold)
        
      • 為什么要分為 Eden 和 Survivor?為什么要設(shè)置兩個(gè) Survivor 區(qū)?

          1、如果沒有 Survivor,Eden 區(qū)每進(jìn)行一次 Minor GC,存活的對象就會被送到老年代。
          
          老年代很快被填滿,觸發(fā) Major GC.老年代的內(nèi)存空間遠(yuǎn)大于新生代,進(jìn)行一次 Full GC
          
          消耗的時(shí)間比 Minor GC 長得多,所以需要分為 Eden和 Survivor。
          
          2、Survivor 的存在意義,就是減少被送到老年代的對象,進(jìn)而減少 Full GC 的
          
          發(fā)生,Survivor 的預(yù)篩選保證,只有經(jīng)歷 16 次 Minor GC 還能在新生代中存
          
          活的對象,才會被送到老年代。
          
          3、設(shè)置兩個(gè) Survivor 區(qū)最大的好處就是解決了碎片化,剛剛新建的對象在
          
          Eden 中,經(jīng)歷一次 Minor GC,Eden 中的存活對象就會被移動到第一塊
          
          survivor space S0,Eden 被清空;等 Eden 區(qū)再滿了,就再觸發(fā)一次 Minor
          
          GC,Eden 和 S0 中的存活對象又會被復(fù)制送入第二塊 survivor space S1(這
          
          個(gè)過程非常重要,因?yàn)檫@種復(fù)制算法保證了 S1 中來自 S0 和 Eden 兩部分的
          
          存活對象占用連續(xù)的內(nèi)存空間,避免了碎片化的發(fā)生)
        

      JVM 中一次完整的 GC 流程是怎樣的,對象如何晉升到老年代

      思路:先描述一下 Java 堆內(nèi)存劃分,再解釋 Minor GC,Major GC,full GC,描述它們之間轉(zhuǎn)

      化流程。


      答:

      • Java 堆 = 老年代 + 新生代

      • 新生代 = Eden + S0 + S1

      • 當(dāng) Eden 區(qū)的空間滿了, Java 虛擬機(jī)會觸發(fā)一次 Minor GC,以收集新生代的垃圾,存活

        下來的對象,則會轉(zhuǎn)移到 Survivor 區(qū)。

      • 大對象(需要大量連續(xù)內(nèi)存空間的 Java 對象,如那種很長的字符串)直接進(jìn)入老年態(tài)

      • 如果對象在 Eden 出生,并經(jīng)過第一次 Minor GC 后仍然存活,并且被Survivor 容納的

        話,年齡設(shè)為 1,每熬過一次 Minor GC,年齡+1,若年齡超過一定限制(15),則被晉

        升到老年態(tài)。即長期存活的對象進(jìn)入老年態(tài)

      • 老年代滿了而無法容納更多的對象,Minor GC 之后通常就會進(jìn)行 Full GC,F(xiàn)ull GC 清理

        整個(gè)內(nèi)存堆 – 包括年輕代和年老代。

      • Major GC 發(fā)生在老年代的 GC清理老年區(qū),經(jīng)常會伴隨至少一次 MinorGC,比 Minor

        GC 慢 10 倍以上


      JVM 中的永久代中會發(fā)生垃圾回收嗎

      垃圾回收不會發(fā)生在永久代,如果永久代滿了或者是超過了臨界值,會觸發(fā)完全垃圾回收(Full

      GC)。如果你仔細(xì)查看垃圾收集器的輸出信息,就會發(fā)現(xiàn)永久代也是被回收的。這就是為什么正

      確的永久代大小對避免 Full GC 是非常重要的原因。請參考下 Java8:從永久代到元數(shù)據(jù)區(qū)。


      (譯者注:Java8 中已經(jīng)移除了永久代,新加了一個(gè)叫做元數(shù)據(jù)區(qū)的 native 內(nèi)存區(qū))


      JAVA8 與元數(shù)據(jù)


      在 Java8 中, 永久代已經(jīng)被移除,被一個(gè)稱為“元數(shù)據(jù)區(qū)”(元空間)的區(qū)域所取代。元空間

      的本質(zhì)和永久代類似,元空間與永久代之間最大的區(qū)別在于:元空間并不在虛擬機(jī)中,而是使用

      本地內(nèi)存。因此,默認(rèn)情況下,元空間的大小僅受本地內(nèi)存限制。 類的元數(shù)據(jù)放入 native

      memory, 字符串池和類的靜態(tài)變量放入 java 堆中, 這樣可以加載多少類的元數(shù)據(jù)就不再由

      MaxPermSize 控制, 而由系統(tǒng)的實(shí)際可用空間來控制。


      如何判斷對象可以被回收?

      判斷對象是否存活一般有兩種方式:

      • 引用計(jì)數(shù):

        每個(gè)對象有一個(gè)引用計(jì)數(shù)屬性,新增一個(gè)引用時(shí)計(jì)數(shù)加 1,引用釋放時(shí)計(jì)數(shù)減1,計(jì)數(shù)為 0

        時(shí)可以回收。此方法簡單,無法解決對象相互循環(huán)引用的問題。

      • 可達(dá)性分析(Reachability Analysis):

        從 GC Roots 開始向下搜索,搜索所走過的路徑稱為引用鏈。當(dāng)一個(gè)對象到GC Roots 沒有

        任何引用鏈相連時(shí),則證明此對象是不可用的,不可達(dá)對象。


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

      在 Java 中,引用和對象是有關(guān)聯(lián)的。如果要操作對象則必須用引用進(jìn)行。因此,很顯然一個(gè)簡

      單的辦法是通過引用計(jì)數(shù)來判斷一個(gè)對象是否可以回收。簡單說,即一個(gè)對象如果沒有任何與之

      關(guān)聯(lián)的引用, 即他們的引用計(jì)數(shù)都不為0, 則說明對象不太可能再被用到,那么這個(gè)對象就是可

      回收對象。


      可達(dá)性分析

      為了解決引用計(jì)數(shù)法的循環(huán)引用問題, Java 使用了可達(dá)性分析的方法。通過一系列的“GC

      roots”對象作為起點(diǎn)搜索。如果在“GC roots”和一個(gè)對象之間沒有可達(dá)路徑,則稱該對象是

      不可達(dá)的。要注意的是,不可達(dá)對象不等價(jià)于可回收對象, 不可達(dá)對象變?yōu)榭苫厥諏ο笾辽僖?jīng)

      過兩次標(biāo)記過程。兩次標(biāo)記后仍然是可回收對象,則將面臨回收。


      Minor GC 與 Full GC 分別在什么時(shí)候發(fā)生?

      新生代內(nèi)存不夠用時(shí)候發(fā)生 MGC 也叫 YGC,JVM 內(nèi)存不夠的時(shí)候發(fā)生 FGC


      垃圾收集算法有哪些類型?

      • GC 最基礎(chǔ)的算法有三類: 標(biāo)記 -清除算法、復(fù)制算法、標(biāo)記-壓縮算法,我們常用的垃圾回

        收器一般都采用分代收集算法。

      • 標(biāo)記 -清除算法,“標(biāo)記-清除”(Mark-Sweep)算法,如它的名字一樣,算法分為“標(biāo)

        記”和“清除”兩個(gè)階段:首先標(biāo)記出所有需要回收的對象,在標(biāo)記完成后統(tǒng)一回收掉所有

        被標(biāo)記的對象。

      • 復(fù)制算法,“復(fù)制”(Copying)的收集算法,它將可用內(nèi)存按容量劃分為大小相等的兩

        塊,每次只使用其中的一塊。當(dāng)這一塊的內(nèi)存用完了,就將還存活著的對象復(fù)制到另外一塊

        上面,然后再把已使用過的內(nèi)存空間一次清理掉。

      • 標(biāo)記-壓縮算法,標(biāo)記過程仍然與“標(biāo)記-清除”算法一樣,但后續(xù)步驟不是直接對可回收對

        象進(jìn)行清理,而是讓所有存活的對象都向一端移動,然后直接清理掉端邊界以外的內(nèi)存

      • 分代收集算法,“分代收集”(Generational Collection)算法,把 Java 堆分為新生代和

        老年代,這樣就可以根據(jù)各個(gè)年代的特點(diǎn)采用最適當(dāng)?shù)氖占惴ā?/p>


      說一下 JVM 有哪些垃圾回收算法?

      • 標(biāo)記-清除算法:標(biāo)記無用對象,然后進(jìn)行清除回收。缺點(diǎn):效率不高,無法清除垃圾碎

        片。

      • 復(fù)制算法:按照容量劃分二個(gè)大小相等的內(nèi)存區(qū)域,當(dāng)一塊用完的時(shí)候?qū)⒒钪膶ο髲?fù)制到

        另一塊上,然后再把已使用的內(nèi)存空間一次清理掉。缺點(diǎn):內(nèi)存使用率不高,只有原來的一

        半。

      • 標(biāo)記-整理算法:標(biāo)記無用對象,讓所有存活的對象都向一端移動,然后直接清除掉端邊界

        以外的內(nèi)存。

      • 分代算法:根據(jù)對象存活周期的不同將內(nèi)存劃分為幾塊,一般是新生代和老年代,新生代基

        本采用復(fù)制算法,老年代采用標(biāo)記整理算法。


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

      標(biāo)記無用對象,然后進(jìn)行清除回收。
      標(biāo)記-清除算法(Mark-Sweep)是一種常見的基礎(chǔ)垃圾收集算法,它將垃圾收集分為兩個(gè)階

      段:

      • 標(biāo)記階段:標(biāo)記出可以回收的對象。
      • 清除階段:回收被標(biāo)記的對象所占用的空間。
        標(biāo)記-清除算法之所以是基礎(chǔ)的,是因?yàn)楹竺嬷v到的垃圾收集算法都是在此算法的基礎(chǔ)上進(jìn)

      行改進(jìn)的。


      優(yōu)點(diǎn):實(shí)現(xiàn)簡單,不需要對象進(jìn)行移動。

      缺點(diǎn):標(biāo)記、清除過程效率低,產(chǎn)生大量不連續(xù)的內(nèi)存碎片,提高了垃圾回收的頻率。

      標(biāo)記-清除算法的執(zhí)行的過程如下圖所示:


      image


      復(fù)制算法

      為了解決標(biāo)記-清除算法的效率不高的問題,產(chǎn)生了復(fù)制算法。它把內(nèi)存空間劃為兩個(gè)相等的區(qū)

      域,每次只使用其中一個(gè)區(qū)域。垃圾收集時(shí),遍歷當(dāng)前使用的區(qū)域,把存活對象復(fù)制到另外一個(gè)

      區(qū)域中,最后將當(dāng)前使用的區(qū)域的可回收的對象進(jìn)行回收。


      優(yōu)點(diǎn):按順序分配內(nèi)存即可,實(shí)現(xiàn)簡單、運(yùn)行高效,不用考慮內(nèi)存碎片。

      缺點(diǎn):可用的內(nèi)存大小縮小為原來的一半,對象存活率高時(shí)會頻繁進(jìn)行復(fù)制。

      復(fù)制算法的執(zhí)行過程如下圖所示:


      image


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

      在新生代中可以使用復(fù)制算法,但是在老年代就不能選擇復(fù)制算法了,因?yàn)槔夏甏膶ο蟠婊盥?/p>

      會較高,這樣會有較多的復(fù)制操作,導(dǎo)致效率變低。標(biāo)記- 清除算法可以應(yīng)用在老年代中,但是

      它效率不高,在內(nèi)存回收后容易產(chǎn)生大量內(nèi)存碎片。因此就出現(xiàn)了一種標(biāo)記-整理算法(Mark-

      Compact)算法,與標(biāo)記-整理算法不同的是,在標(biāo)記可回收的對象后將所有存活的對象壓縮到

      內(nèi)存的一端,使他們緊湊的排列在一起,然后對端邊界以外的內(nèi)存進(jìn)行回收。回收后,已用和未

      用的內(nèi)存都各自一邊。


      優(yōu)點(diǎn):解決了標(biāo)記-清理算法存在的內(nèi)存碎片問題。

      缺點(diǎn):仍需要進(jìn)行局部對象移動,一定程度上降低了效率。

      標(biāo)記-整理算法的執(zhí)行過程如下圖所示:


      image


      分代收集算法

      分代收集法是目前大部分 JVM 所采用的方法,其核心思想是根據(jù)對象存活的不同生命周期將內(nèi)

      存劃分為不同的域,一般情況下將 GC 堆劃分為老生代(Tenured/Old Generation)和新生代

      (YoungGeneration)。老生代的特點(diǎn)是每次垃圾回收時(shí)只有少量對象需要被回收,新生代的特點(diǎn)

      是每次垃圾回收時(shí)都有大量垃圾需要被回收,因此可以根據(jù)不同區(qū)域選擇不同的算法。


      當(dāng)前商業(yè)虛擬機(jī)都采用分代收集的垃圾收集算法。分代收集算法,顧名思義是根據(jù)對象的存活周

      將內(nèi)存劃分為幾塊。一般包括年輕代、老年代 和 永久代

      如圖所示:


      image


      當(dāng)前主流 VM 垃圾收集都采用”分代收集” (Generational Collection)算法, 這種算法會根據(jù)對

      象存活周期的不同將內(nèi)存劃分為幾塊, 如 JVM 中的 新生代、老年代、永久代, 這樣就可以根據(jù)

      各年代特點(diǎn)分別采用最適當(dāng)?shù)?GC 算法。


      新生代與復(fù)制算法

      每次垃圾收集都能發(fā)現(xiàn)大批對象已死, 只有少量存活. 因此選用復(fù)制算法, 只需要付出少量存活對

      象的復(fù)制成本就可以完成收集。

      目前大部分 JVM 的 GC 對于新生代都采取 Copying 算法,因?yàn)樾律忻看卫厥斩家厥?/p>

      大部分對象,即要復(fù)制的操作比較少,但通常并不是按照 1: 1 來劃分新生代。一般將新生代劃

      分為一塊較大的 Eden 空間和兩個(gè)較小的 Survivor 空間(From Space, To Space),每次使用

      Eden 空間和其中的一塊 Survivor 空間,當(dāng)進(jìn)行回收時(shí),將該兩塊空間中還存活的對象復(fù)制到另

      一塊 Survivor 空間中。


      image


      老年代與標(biāo)記復(fù)制算法

      因?yàn)槔夏甏鷮ο蟠婊盥矢摺]有額外空間對它進(jìn)行分配擔(dān)保, 就必須采用“標(biāo)記—清理”或“標(biāo)

      記—整理” 算法來進(jìn)行回收, 不必進(jìn)行內(nèi)存復(fù)制, 且直接騰出空閑內(nèi)存。因而采用

      Mark-Compact 算法。

      • JAVA 虛擬機(jī)提到過的處于方法區(qū)的永生代(Permanet Generation), 它用來存儲 class

        類,常量,方法描述等。對永生代的回收主要包括廢棄常量和無用的類

      • 對象的內(nèi)存分配主要在新生代的 Eden Space 和 Survivor Space 的From Space(Survivor

        目前存放對象的那一塊),少數(shù)情況會直接分配到老生代。

      • 當(dāng)新生代的 Eden Space 和 From Space 空間不足時(shí)就會發(fā)生一次 GC,進(jìn)行 GC 后,

        EdenSpace 和 From Space 區(qū)的存活對象會被挪到 To Space,然后將 Eden Space 和

        FromSpace 進(jìn)行清理。

      • 如果 To Space 無法足夠存儲某個(gè)對象,則將這個(gè)對象存儲到老生代。

      • 在進(jìn)行 GC 后,使用的便是 Eden Space 和 To Space 了,如此反復(fù)循環(huán)。

      • 當(dāng)對象在 Survivor 區(qū)躲過一次 GC 后,其年齡就會+1。 默認(rèn)情況下年齡到達(dá) 15 的對象會

        被移到老生代中。


      GC 垃圾收集器

      Java 堆內(nèi)存被劃分為新生代和年老代兩部分,新生代主要使用復(fù)制和標(biāo)記-清除垃圾回收算法;

      年老代主要使用標(biāo)記-整理垃圾回收算法,因此 java 虛擬中針對新生代和年老代分別提供了多種

      不同的垃圾收集器, JDK1.6 中 SunHotSpot 虛擬機(jī)的垃圾收集器如下:


      image


      說一下 JVM 有哪些垃圾回收器?

      如果說垃圾收集算法是內(nèi)存回收的方法論,那么垃圾收集器就是內(nèi)存回收的具體實(shí)現(xiàn)。下圖展示

      了 7 種作用于不同分代的收集器,其中用于回收新生代的收集器包括 Serial、PraNew、Parallel

      Scavenge,回收老年代的收集器包括Serial Old、Parallel Old、CMS,還有用于回收整個(gè) Java

      堆的 G1 收集器。不同收集器之間的連線表示它們可以搭配使用。


      image


      • Serial 收集器(復(fù)制算法): 新生代單線程收集器,標(biāo)記和清理都是單線程,優(yōu)點(diǎn)是簡單高效;

      • ParNew 收集器 (復(fù)制算法): 新生代收并行集器,實(shí)際上是 Serial 收集器的多線程版本,在

        多核 CPU 環(huán)境下有著比 Serial 更好的表現(xiàn);

      • Parallel Scavenge 收集器 (復(fù)制算法): 新生代并行收集器,追求高吞吐量,高效利用

        CPU。吞吐量 = 用戶線程時(shí)間/(用戶線程時(shí)間+GC 線程時(shí)間),高吞吐量可以高效率的利用

        CPU 時(shí)間,盡快完成程序的運(yùn)算任務(wù),適合后臺應(yīng)用等對交互相應(yīng)要求不高的場景;

      • Serial Old 收集器 (標(biāo)記-整理算法): 老年代單線程收集器,Serial 收集器的老年代版本;

      • Parallel Old 收集器 (標(biāo)記-整理算法): 老年代并行收集器,吞吐量優(yōu)先,

        ParallelScavenge 收集器的老年代版本;

      • CMS(Concurrent Mark Sweep)收集器(標(biāo)記-清除算法): 老年代并行收集器,以獲取最

        短回收停頓時(shí)間為目標(biāo)的收集器,具有高并發(fā)、低停頓的特點(diǎn),追求最短GC 回收停頓時(shí)間。

      • G1(Garbage First)收集器 (標(biāo)記-整理算法): Java 堆并行收集器,G1 收集器是JDK1.7 提

        供的一個(gè)新收集器,G1 收集器基于“標(biāo)記-整理”算法實(shí)現(xiàn),也就是說不會產(chǎn)生內(nèi)存碎片。

        此外,G1 收集器不同于之前的收集器的一個(gè)重要特點(diǎn)是:G1 回收的范圍是整個(gè) Java 堆(包

        括新生代,老年代),而前六種收集器回收的范圍僅限于新生代或老年代。


      Serial 與 Parallel GC 之間的不同之處?

      Serial 與 Parallel 在 GC 執(zhí)行的時(shí)候都會引起 stop-the-world。它們之間主要不同 serial 收集

      器是默認(rèn)的復(fù)制收集器,執(zhí)行 GC 的時(shí)候只有一個(gè)線程,而 parallel 收集器使用多個(gè) GC 線程來

      執(zhí)行。


      posted @ 2024-06-05 17:55  二價(jià)亞鐵  閱讀(96)  評論(0)    收藏  舉報(bào)
      主站蜘蛛池模板: 无码国产偷倩在线播放老年人| 国产精品美女一区二三区| 无码日韩精品一区二区三区免费| 无码粉嫩虎白一线天在线观看| 日本伊人色综合网| 少妇人妻偷人免费观看| 国内熟女中文字幕第一页| 国产AV无码专区亚洲AV漫画| 少妇精品视频一码二码三| 亚洲成人免费一级av| 午夜福利啪啪片| 少妇被日自拍黄色三级网络| 久久精品国产99国产精品澳门| av午夜久久蜜桃传媒软件| 日本内射精品一区二区视频| 亚洲成人资源在线观看| 亚洲人成网站色7799| 日韩精品专区在线影院重磅| 亚洲精品人成网线在播放VA| 亚洲精品熟女一区二区| 一区天堂中文最新版在线| 久久99国产精品久久99| 亚洲精品综合网二三区| 亚洲AV无码AV在线影院 | 宝兴县| 日本深夜福利在线观看| 国产精品美女AV免费观看| 人妻日韩精品中文字幕| 高清在线一区二区三区视频| AV无码免费不卡在线观看| 日韩中文字幕有码午夜美女| 熟妇人妻一区二区三区四区| 女的被弄到高潮娇喘喷水视频| 高清偷拍一区二区三区| 中文字幕人妻不卡精品| 日韩免费美熟女中文av| 欧美三级不卡在线观线看高清| 成av免费大片黄在线观看| 久久国产免费观看精品3| 蓬溪县| 久久青青草原亚洲AV无码麻豆|