JVM調優學習筆記

1、Part 1

一、JVM垃圾回收的時候如何肯定垃圾?是否知道什麼是GC Roots

  1. 什麼是垃圾?java

    內存中已經再也不使用到的空間就是垃圾linux

  2. 要進行垃圾回收,如何判斷一個對象是否能夠被回收ios

    • 引用計數法:java中,引用和對象是由關聯的。若是要操做對象則必須用引用進行。算法

      所以很顯然一個簡單的辦法是經過引用計數來判斷一個對象是否能夠回收,簡單說,給對象中添加一個引用計數器,每當有一個地方引用它,計數器加1,每當有一個引用失效時,計數器減1,任什麼時候刻計數器數值爲零的對象就是不可能再被使用的,那麼這個對象就是可回收對象。緩存

      可是它很難解決對象之間相互循環引用的問題服務器

      JVM通常不採用這種實現方式。網絡

    • ==枚舉根節點作可達性分析(跟搜索路徑)==多線程

      爲了解決引用計數法的循環引用問題,java使用了可達性分析的方法。併發

      所謂GC ROOT或者說Tracing GC的「根集合」就是一組比較活躍的引用。負載均衡

      基本思路就是經過一系列「GC Roots」的對象做爲起始點,從這個被稱爲GC Roots 的對象開始向下搜索,若是一個對象到GC Roots沒有任何引用鏈相連時,則說明此對象不可用。也即給定一個集合的引用做爲根出發,經過引用關係遍歷對象圖,能被便利到的對象就被斷定爲存活;沒有被便利到的就被斷定爲死亡

(1)哪些對象能夠做爲GC Roots對象

  • 虛擬機棧(棧幀中的局部變量區,也叫局部變量表)中應用的對象。
  • 方法區中的類靜態屬性引用的對象
  • 方法區中常量引用的對象
  • 本地方法棧中JNI(native方法)引用的對象

二、如何盤點查看JVM系統默認值

如何查看運行中程序的JVM信息

jps查看進程信息

jinfo -flag 配置項 進程號

jinfo -flags 進程號 ==== 查看全部配置

(1)JVM參數類型

  1. 標配參

    -version -help

    各個版本之間穩定,不多有很大的變化

  2. x參數

    -Xint -Xcomp -Xmixed

    • -Xint :解釋執行
    • -Xcomp:第一次使用就編譯成本地代碼
    • -Xmixed:混合模式
  3. ==xx參數==

    • Boolean類型

      公式:-XX+或者-某個屬性值 +表示開啓,-表示關閉

      是否打印GC收集細節 -XX:+PrintGCDetails 開啓 -XX:-PrintGCDetails 關閉

      是否使用串行垃圾回收器:-XX:-UseSerialGC

    • KV設值類型

      公式:-XX:屬性key=屬性值value

      case:

      -XX:MetaspaceSize=128m

      -XX:MaxTenuringThreshold=15

      -Xms----> -XX:InitialHeapSize

      -Xmx----> -XX:MaxHeapSize

(2)查看參數

  • -XX:+PrintFlagsInitial

    查看初始默認

    ==java -XX:+PrintFlagsInitial -version==

  • -XX:+PrintFlagsFinal

    查看修改後的 :=說明是修改過的

  • -XX:+PrintCommandLineFlags

    查看使用的垃圾回收器

三、你平時工做用過的JVM經常使用基本配置參數有哪些

-Xms -Xmx -Xmn

-Xms128m -Xmx4096m -Xss1024K -XX:MetaspaceSize=512m -XX:+PrintCommandLineFlags -XX:+PrintGCDetails -XX:+UseSerialGC

  1. -Xms

    初始大小內存,默認爲物理內存1/64,等價於-XX:InitialHeapSize

  2. -Xmx

    最大分配內存,默認物理內存1/4,等價於-XX:MaxHeapSize

  3. -Xss

    設置單個線程棧的大小,默認542K~1024K ,等價於-XX:ThreadStackSize

  4. -Xmn

    設置年輕代的大小

  5. -XX:MetaspaceSize

    設置元空間大小

    元空間的本質和永久代相似,都是對JVM規範中方法區的實現,不過元空間與永久代最大的區別在於:==元空間並不在虛擬機中,而是在本地內存中。==所以,默認元空間的大小僅受本地內存限制

  6. -XX:+PrintGCDetails

    輸出詳細GC收集日誌信息

    [名稱:GC前內存佔用->GC後內存佔用(該區內存總大小)]

  7. -XX:SurvivorRatio

    設置新生代中Eden和S0/S1空間的比例

    默認-XX:SurvivorRatio=8,Eden:S0:S1=8:1:1

  8. -XX:NewRatio

    設置年輕代與老年代在堆結構的佔比

    默認-XX:NewRatio=2 新生代在1,老年代2,年輕代佔整個堆的1/3

    NewRatio值幾句詩設置老年代的佔比,剩下的1給新生代

  9. -XX:MaxTenuringThreshold

    設置垃圾的最大年齡

    默認-XX:MaxTenuringThreshold=15

    若是設置爲0,年輕代對象不通過Survivor區,直接進入年老代。對於年老代比較多的應用,能夠提升效率。若是將此值設置爲一個較大的值,則年輕代對象回在Survivor區進行屢次複製,這樣能夠增長對對象在年輕代的存活時間,增長在年輕代即被回收的機率。

  10. -XX:+UseSerialGC

    串行垃圾回收器

  11. -XX:+UseParallelGC

    並行垃圾回收器

四、強引用、軟引用、弱引用、虛引用做用分別是什麼

4.1 強引用 Reference

當內存不足,JVM開始垃圾回收,對於強引用對象,就算出現了OOM也不會堆該對象進行回收。

強引用是咱們最多見的普通對象引用,只要還有強引用指向一個對象,就能表面對象還「或者」,垃圾收集器不會碰這種對象。在Java中最多見的就是強引用,把一個對象賦給一個引用變量,這個引用變量就是一個強引用。當一個對象被強引用變量引用時,它處於可達狀態,他是不可能被垃圾回收機制回收的,既是該對象之後永遠都不會被用到 JVM也不會回收。所以強引用時形成java內存泄漏的主要緣由之一。

對於一個普通對象,若是沒有其餘的引用關係,只要超過了引用的做用域或者顯式的將應用(強)引用複製爲null,通常認爲就是能夠被垃圾收集的了

4.2 軟引用 SoftReference

軟引用是一種相對強引用弱化了一些做用,須要用java.lang.ref.SOftReference類來實現,可讓對象豁免一些垃圾收集。

當系統內存充足時他不會被回收,當內存不足時會被回收。

軟引用一般在對內存敏感的程序中,好比高速緩存就有用到軟引用,內存足夠的時候就保留,不夠就回收。

4.3 弱引用 WeakReference

弱引用須要用java.lang.ref.WeakReference類來實現,比軟引用的生存期更短

只要垃圾回收機制一運行,無論JVM的內存空間是否足夠,都會回收。

  • 談談WeakHashMap

    key是弱引用

4.4 虛引用PhantomReference

顧名思義,就是形同虛設,與其餘集中不一樣,虛引用並不會決定對象的生命週期

若是一個對象持有虛引用,那麼他就和沒有任何引用同樣,在任什麼時候候均可能被垃圾回收器回收,他不能單獨使用也不能經過它訪問對象,虛引用和引用隊列(ReferenceQueeu)聯合使用。

需應用的主要做用是跟蹤對象被垃圾回收的狀態。僅僅是提供了一種確保ui想被finalize之後,作某些事情的機制。

PhantomReference的get方法老是返回null,所以沒法訪問對應的引用對象。其意義在於說明一個對象已經進入finalization階段,能夠被gc回收,用來實現比finalization機制更靈活的回收操做

換句話說,設置虛引用關聯的惟一目的,就是這個對象被收集器回收的時候收到一個系統通知或者後續添加進一步的處理。

java容許使用finalize()方法在垃圾收集器將對象從內存中清除出去以前作必要的清理工做。

4.4.1 引用隊列Reference

建立引用的時候能夠指定關聯的隊列,當gc釋放對象內存的時候,會把引用加入到引用隊列,若是程序發現某個虛引用已經被加入到引用隊列,那麼就能夠在所引用的對象的內存被回收以前採起必要的行動,至關於通知機制

當關聯的引用隊列中有數據的時候,意味着引用指向的對內存中的對象被回收。經過這種方式,jvm容許咱們在對象被小回收,作一些咱們本身想作的事情。

4.5 適用場景

  • 加入一個應用須要讀取大量的本地圖片,若是每次讀取圖片都從硬盤讀取會嚴重影響性能,若是一次性所有加載到內存又可能形成內存溢出,這時能夠用軟引用解決這個問題

    設計思路:用一個HashMap來保存圖片路徑和相應圖片對象關聯的軟引用之間的映射關係,在內存不足時,JVM會自動共回收這些緩存圖片對象所佔的空間,避免OOM

    Map<String, SoftReference<Bitmap>> imageCache = new HashMap<>();
    複製代碼

五、請你談談對OOM的認識

  • java.lang.StackOverflowError

    棧空間溢出 ,遞歸調用卡死

  • java.lang.OutOfMemoryError:Java heap space

    堆內存溢出 , 對象過大

  • java.lang.OutOfMemoryError:GC overhead limit exceeded

    GC回收時間過長

    過長的定義是超過98%的時間用來作GC而且回收了而不倒2%的堆內存

    連續屢次GC,都回收了不到2%的極端狀況下才會拋出

    若是不拋出,那就是GC清理的一點內存很快會被再次填滿,迫使GC再次執行,這樣就惡性循環,

    cpu使用率一直是100%,二GC卻沒有任何成果

    int i = 0;
    List<String> list = new ArrayList<>();
    try{
        while(true){
            list.add(String.valueOf(++i).intern());
        }
    }catch(Throwable e){
        System.out.println("********");
        e.printStackTrace();
        throw e;
    }
    複製代碼
  • java.lang.OutOfMemoryError:Direct buffer memory

    執行內存掛了

    寫NIO程序常用ByteBuffer來讀取或寫入數據,這是一種基於通道(Channel)與緩存區(Buffer)的I/O方式,它可使用Native函數庫直接分配堆外內存,而後經過一個存儲在java堆裏面的DirectByteBuffer對象做爲這塊內存的引用進行操做,這樣能在一些場景中顯著提升性能,由於避免了在java堆和native堆中來回複製數據

    ByteBuffer.allocate(capability) 第一種方式是分配JVM堆內存,屬於GC管轄,因爲須要拷貝因此速度較慢

    ByteBuffer.alloctedDirect(capability)分配os本地內存,不屬於GC管轄,不須要拷貝,速度較快

    但若是不斷分配本地內存,堆內存不多使用,那麼jvm就不須要執行GC,DirectByteBuffer對象們就不會被回收,這時候堆內存充足,但本地內存可能已經使用光了,再次嘗試分配本地內存救護i出現oom,程序崩潰

  • java.lang.OutOfMemoryError:unable to create new native thread==好案例==

    • 應用建立了太多線程,一個應用進程建立了多個線程,超過系統承載極限
    • 你的服務器並不容許你的應用程序建立這麼多線程,linux系統默認容許單個進程能夠建立的線程數是1024,超過這個數量,就會報錯

    解決辦法

    下降應用程序建立線程的數量,分析應用給是否針對須要這麼多線程,若是不是,減到最低

    修改linux服務器配置

  • java.lang.OutOfMemoryError:Metaspace

    元空間主要存放了虛擬機加載的類的信息、常量池、靜態變量、即時編譯後的代碼

    static class OOMTest{}
    public static void main(String[] args){
        int i = 0;
        try{
           while(true){
               i++;
               Enhancer enhancer = new Enhancer();
               enhancer.setSuperclass(OOMTest.class);
               enhancer.setUseCache(false);
               enhancer.setCallback(new MethodInterceptor(){
                   @Override
                   public Object intercept(Object o,Method method,Object[] objects, MethodProxy methodProxy)throws Throwable{
                       return methodProxy.invokeSuper(o,args);
                   }
               });
               enhancer.create();
           } 
        } catch(Throwable e){
            System.out.println(i+"次後發生了異常");
            e.printStackTrace();
        }
    }
    複製代碼

六、==GC垃圾回收算法和垃圾收集器的關係?分別是什麼==

垃圾收集器就是算法的具體實現

6.1 GC算法

  • 引用計數
  • 複製
  • 標記清理
  • 標記整理

6.2 4種主要垃圾收集器

  1. Serial 串行回收

    爲單線程換進該設計且只是用過一個線程進行垃圾回收,會暫停全部的用戶線程,不適合服務器環境

  2. Paralle 並行回收

    多個垃圾收集線程並行工做,此時用戶線程是暫停的,適用於科學計算/大數據處理首臺處理等弱交互場景

  3. CMS 併發標記清除

    用戶線程和垃圾手機線程同時執行(不必定是並行,可能交替執行),不須要停頓用戶線程,互聯網公司多用它,適用堆響應時間有要求的場景

  4. G1

    將堆內存分割城不一樣的區域而後併發的對其進行垃圾回收

  5. ZGC

七、==怎麼查看服務器默認的垃圾收集器是哪一個?生產上如何配置垃圾收集器?對垃圾收集器的理解?==

  1. 查看:java -XX:+PrintCommandLinedFlags -version

  2. 配置:

    有七種:UseSerialGC UseParallelGC UseConcMarkSweepGC UseParNewGC UseParallelOldGC UseG1GC

八、==垃圾收集器==

8.1 部分參數說明

  1. DefNew:Default New Generation
  2. Tenured:Old
  3. ParNew:Parallel New Generation
  4. PSYoungGen:Parallel Scavenge
  5. ParOldGen:Parallel Old Generation

8.2 Server/Client模式分別是什麼意思

  1. 適用範圍:只須要掌握Server模式便可,Client模式基本不會用
  2. 操做系統
    • 32位Window操做系統,不論硬件如何都默認使用Client的JVM模式
    • 32位其餘操做系統,2G內存同時有2個cpu以上用Server模式,低於該配置仍是Client模式
    • 64位only server模式

8.3 新生代

  1. 串行GC (Serial)/(Serial Copying)

    串行收集器:Serial收集器 1:1

    一個單線程的收集器,在進行垃圾收集的時候,必須暫停其餘全部的工做線程知道它收集結束。

    串行收集器是最古老,最穩定以及效率最高的收集器,只使用一個線程去回收但其在進行垃圾收集過程當中可能會產生較長的停頓(Stop-The-World 狀態)。雖然在收集垃圾過程當中須要暫停全部其餘的工做線程,可是它簡單高效,==對於限定單個CPU環境來講,沒有線程交互的開銷能夠得到最高的單線程垃圾收集效率,所以Serial垃圾收集器依然是java虛擬機運行在Client模式下默認的新生代垃圾收集器。==

    對應的JVM參數是:==-XX:+UseSerialGC==

    ==開啓後會使用:Serial(Young區)+Serial Old(Old區)的收集器組合==

    表示:==新生代、老年代都會使用串行回收收集器,新生代使用複製算法,老年代使用標記整理算法==

    DefNew ----- Tenured

  2. 並行GC (ParNew)

    並行收集器 N:1

    使用多線程進行垃圾回收,在垃圾收集時,會Stop-the-world暫停其餘全部的工做線程知道它收集結束。

    ==ParNew收集器其實就是Serial收集器新生代的並行多線程版本,==最多見的應用場景時配合==老年代的CMS GC工做==,其他的行爲和Serial收集器徹底同樣,ParNew垃圾收集器在垃圾收集過程成中童謠也要暫停全部其餘的工做線程,他是不少==java虛擬機運行在Server模式下新生代的默認垃圾收集器。==

    經常使用對應JVM參數:==-XX:+UseParNewGC== 啓用ParNew收集器,隻影響新生代的收集,不影響老年代

    ==開啓後會使用:ParNew(Young區)+Serial Old(Old 區)的收集器組合==,新生代使用複製算法,老年代採用標記-整理算法

    ParNew ------ Tenured

    備註:

    ==-XX:ParallelGCThreads== 限制線程數量,默認開啓和CPU數目相同的線程數

  3. 並行回收GC (Parallel)/(Parallel Scavenge)

    並行收集器 N:N

    相似ParNew也是一個新生代垃圾收集器,使用複製算法,也是一個並行的多線程的垃圾收集器,俗稱吞吐量有限收集器。串行收集器在新生代和老年代的並行化。

    他重點關注的是:

    • 可控制的吞吐量(Thoughput=運行用戶代碼時間/(運行用戶代碼時間+垃圾收集時間),業績好比程序運行100分鐘,垃圾收集時間1分鐘,吞吐量就是99%)。高吞吐量意味着高效利用CPU時間,他多用於在後臺運算而不須要太多交互的任務。
    • 自適應調節策略也是ParallelScavenge收集器與ParNew收集器的一個重要區別。(自適應調節策略:虛擬機會根據當前系統的運行狀況手機性能監控信息,動態調整這些參數以提供最合適的停頓時間==(-XX:MaxGCPauseMillis)==或最大的吞吐量

    ==-XX:ParallelGCThreads=數字N== 表示啓動多少個GC線程

    cpu>8 n=5/8

    cpu<8 n=實際個數

    ==-XX:+UseParallelGC==、==-XX:+UseParallelOldGC==

8.4 老年代

  1. 串行GC(Serial Old)/(Serial MSC)

  2. 並行GC(Parallel Old)/(Parallel MSC)

    Parallel Scavenge的老年代版本,使用多線程的標記-整理算法,Parallel Old收集器在JDK1.6開始提供

    1.6以前,新生代使用ParallelScavenge收集器只能搭配年老代的Serial Old收集器,只能保證新生代的吞吐量優先,沒法保證總體的吞吐量。在1.6以前(ParallelScavenge+SerialOld)

    Parallel Old正式爲了在老年代一樣提供吞吐量遊戲的垃圾收集器,若是系統對吞吐量要求比較高,JDK1.8後能夠考慮新生代Parallel Scavenge和年老代Parallel Old收集器的搭配策略

    經常使用參數:==-XX:+UseParallelOldGC==》》》》》新生代Paralle+老年代Paralle Old

  3. 併發標記清除GC(CMS)

    CMS收集器(Concurrent Mark Sweep:併發標記清除)是一種以獲取最短回收停頓時間爲目標的收集器。

    適合在互聯網站或者B/S系統的服務器上,這列應用尤爲中使服務器的響應速度,但願系統停頓時間最短。

    CMS很是適合堆內存大、CPU核數多的服務區端應用,也是G1出現之大型應用的首選收集器。

    併發標記清除收集器:ParNew+CMS+Serial Old

    CMS,併發收集低停頓,併發指的是與用戶線程一塊兒執行

    JVM參數:==-XX:+UseConcMarkSweepGC==,開啓該參數後會自動將-XX:UseParNewGC打開

    開啓該參數後,使用ParNew+CMS+Serial Old的收集器組合,Serial Old將做爲CMS出錯的後備收集器

    4步過程:

    • 初始標記(CMS initial mark)

      只是標記一下GC Roots可以直接關聯的對象,速度很快,仍然須要暫停全部的工做線程。

    • 併發標記(CMS concurrent mark)和用戶線程一塊兒

      進行GC Roots跟蹤過程,和用戶線程一塊兒工做,不須要暫停工做線程。主要標記過程,標記所有對象

    • 從新標記(CMS remark)

      爲了修正併發標記期間,因用戶程序繼續運行而致使標記產生變更的那一部分對象的標記記錄,仍然須要暫停全部的工做線程,因爲併發標記時,用戶線程依然運行,所以在正式清理前,再作修正

    • 併發清除(CMS concurrent sweep)和用戶線程一塊兒

      清除GC Roots不可達對象,和用戶線程一塊兒工做,不須要暫停工做線程。基於標記結果,直接清理對象

      因爲耗時最長的併發標記和併發清除過程當中,垃圾收集線程能夠和用戶如今一塊兒併發工做,因此整體上看來CMS收集器的內存回收和用戶線程是一塊兒併發的執行。

    優缺點:

    • 併發收集停頓低

    • 併發執行,cpu資源壓力大

      因爲併發進行,CMS在收集與應用線程會同時增長對堆內存的佔用,也就是說,==CMS必需要在老年代堆內存用盡以前完成垃圾回收==,不然CMS回收失敗時,將出發擔保機制,串行老年代收集器將會以STW的方式進行一次GC,從而形成較大停頓時間。

    • 採用的標記清除算法會致使大量的碎片

      標記清除算法沒法整理空間碎片,老年代空間會隨着應用時長被逐步耗盡,最後將不得不經過擔保機制堆堆內存進行壓縮。CMS也提供了參數==-XX:CMSFullGCsBeForeCompaction==(默認0,即每次都進行內存整理)來制定多少次CMS收集以後,進行一次壓縮的FullGC。

8.5 如何選擇垃圾選擇器

  • 單CPU或小內存,單機內存

    -XX:+UseSerialGC

  • 多CPU,須要最大吞吐量,如後臺計算型應用

    -XX:+UseParallelGC -XX:+UseParallelOldGC

  • 多CPU,最求低停頓時間,需快速相應,如互聯網應用

    -XX:+ParNewGC -XX:+UseConcMarkSweepGC

參數 新生代垃圾收集器 新生代算法 老年代垃圾收集器 老年代算法
UseSerialGC SerialGC 複製 SerialOldGC 標整
UseParNewGC ParNew 複製 SerialOldGC 標整
UseParallelGC
UseParallelOldGC
Parallel[Scavenge] 複製 Parallel Old 標整
UseConcMarkSweepGC ParNew 複製 CMS+Serial Old的收集器組合(Serial Old 做爲CMS出錯的後備收集器) 標清
UseG1GC G1總體上採用標整 局部是經過複製算法

九、==G1垃圾收集器==

將堆內存分割城不一樣的區域而後併發的對其進行垃圾回收

9.1 其餘收集器特色

  • 年輕代和老年代是各自獨立且了連續的內存塊
  • 年輕代收集使用單eden+S0+S1進行復制算法
  • 老年代收集必須掃描真個老年代區域
  • 都是以儘量少而快速地執行GC爲設計原則

9.2 G1是什麼

G1(Garbage-First)收集器,是一款面向服務端應用的收集器

應用在多處理器和大容量內存環境中,在實現高吞吐量的同時,儘量的知足垃圾收集暫停時間的要求

  • 和CMS收集器同樣,能與應用程序線程併發執行
  • 整理空閒空間更快
  • 須要更多的時間來預測GC停頓時間
  • 不但願犧牲大量的吞吐性能
  • 不須要更大的Java Heap

G1收集器的設計目標是取代CMS收集器,和CMS相比,在如下放木表現更出色:

  • G1是一個由整理內存過程的垃圾收集器,不會產生不少內存碎片
  • G1的Stop The World(STW)更可控,G1在停頓時間上添加了預測機制,用戶能夠指按期望停頓時間。

CMS垃圾收集器雖然減小了暫停應用程序的運行時間,可是他仍是存在着內存碎片問題。因而爲了取出內存碎片問題,同時又保留CMS垃圾收集器低暫停時間的優勢,JAVA7發佈了G1垃圾收集器。

主要改變的是Eden,Survivor和Tenured等內存區域再也不是連續的了,而是變成了一個個大小同樣的region(區域化),每一個region從1M到32M不等。一個region有可能屬於Eden,Survivor或Tenured內存區域。

9.2.1 特色
  1. G1能充分利用多CPU、多核環境優點,儘可能縮短STW。
  2. G1總體上採用標記-整理算法,局部是經過複製算法,不會產生內存碎片。
  3. 宏觀上G1之中再也不區分年輕代和老年代。把內存劃分紅多個獨立的子區域(Region),能夠近似理解爲一個棋盤
  4. G1收集器裏面講整個的內存去都混合在一塊兒了,但其自己依然在小範圍內要進行年輕代和老年代的區分,保留了新生代和老年代,但它們再也不是物理隔離,而是一部分Region的集合且不須要Region是連續的,也就是說依然會採用不一樣GC方式來處理不一樣的區域。
  5. G1雖然也是分代收集器,但整個內存分區不存在物理上的年輕代與老年代的區別,也不須要徹底獨立的survivor(to space)堆作複製準備。G1只有邏輯上的分代概念,或者說每一個分區均可能隨G1的運行在不一樣代之間先後切換

9.3 底層原理

  • Region區域化垃圾收集器

    最大好處是化整爲零,避免全內存掃描,只須要按照區域來進行掃描便可

    區域化內存劃片Region,總體編爲了 一系列不連續的內存區域,避免了全內存區的GC操做。

    核心思想是講整個堆內存區域分紅大小相同的子區域,在JVM啓動時會自動共設置這些子區域的大小

    在堆的使用上,G1並不要求對象的存儲必定是物理上連續的,只要邏輯上連續便可,每一個分區也不會固定地爲某個代服務,能夠按需在年輕代和老年代之間切換。啓動時能夠經過參數==-XX:G1HeapRegionSize==可指定分區大小(1~32M,且必須是2的冪),默認將整堆劃分爲2048個分區。

    大小範圍在1-32M,最多能設置2048個區域,也即可以支持的最大內存爲64G

    G1算法將堆劃分爲諾幹個區域,他仍然屬於分代收集器

    • 這些Region的一部分包含新生代,新生代的垃圾收集依然採用暫停全部應用線程的方式,將存活對象拷貝到老年代或Survivor空間,這些Region的一部分包含老年代,G1收集器經過將對象從一個區域複製到另一個區域,完成了清理工做。這就意味着,在正常的處理過程當中,G1完成了堆的壓縮,這樣也就不會有CMS內存碎片問題的存在了

    • 在G1中,還有一種特殊區域,Humongous區域,若是一個對象張勇的空間超過了分區容量50%以上,G1收集器就認爲i這是一個巨型對象。這些巨型對象默認直接會被分配在年老代,可是若是他是一個短時間存在的巨型對象,就會對垃圾收集器形成負面影響。爲了解決這個問題,G1劃分了一個Humongous區,他用來專門存放巨型對象。若是一個H區裝不下,那麼G1就會尋找連續的H分區來存儲。爲了能找到連續的H區,有時候不得不啓動Full GC

  • 回收步驟

    針對Eden區進行收集,Eden區耗盡後會被觸發,主要小區域收集+造成連續的內存塊,避免內存碎片

    • Eden區的數據移動到Survivor區,假如出現Survivor區空間不夠,Eden區數據就會晉升到Old區
    • Survivor區的數據移動到新的Survivor區,部分數據晉升到Old區
    • 最後Eden區收拾乾淨了,GC結束,用戶的應用程序繼續執行。
  • 4步過程

    1. 初始標記:只標記GC Roots能直接關聯到的對象
    2. 併發標記:進行GC Roots Tracing的過程
    3. 最終標記:修正併發標記期間,因程序運行致使標記發生變化的那一部分對象
    4. 篩選回收:根據時間來進行價值最大化的回收

9.4 case

9.5 經常使用配置參數(不是重點)

  • -XX:+UseG1Gc

  • -XX:G1HeapRegionSize=n

    設置的G1區域的大小,值是2的冪,範圍是1-32MB,目標是根據最小的java堆大小劃分出約2048個區域

  • -XX:MaxGCPauseMillis=n

    最大GC停頓時間,這是個軟目標,JVM將盡量(但不保證)停頓小於這個時間

  • -XX:InitiatingHeapOccupancyRercent=n

    堆佔用了多少的時候就觸發GC,默認45

  • -XX:ConcGcThreads=n

    併發GC使用的線程數

  • -XX:G1ReservePercent=n

    設置做爲空閒空間的預留內存百分比,以下降目標空間溢出的風險,默認10%

9.6 和CMS相比優點

  1. G1不會產生內存碎片
  2. 能夠精確控制停頓。該收集器十八真個堆劃分紅多個固定大小的區域,每根據容許停頓的時間去收集垃圾最多的區域

十、生產環境服務器變慢,診斷思路和性能評估談談?

  1. 整機:top 系統性能

    uptime 精簡版

    load average:系統負載均衡 1min 5min 15min 系統的平均負載值 相加/3>60%壓力夠

  2. CPU:vmstat

    • 查看CPU

      vmstat -n 2 3 第一個參數是採樣的時間間隔數單位s,第二個參數是採樣的次數

      • procs

        r:運行和等待CPU時間片的進程數,原則上1核CPu的運行隊列不要超過2,真個系統的運行隊列不能超過總核數的2倍,不然表示系統壓力過大

        b:等待資源的進程數,好比正在等待磁盤I/O,網絡I/O等

      • cpu

        us:用戶進程消耗cpu時間百分比,us高,用戶進程消耗cpu時間多,若是長期大於50%,優化程序

        sy:內核進程消耗的cpu時間百分比

        us+sy:參考值爲80%,若是大於80,說明可能存在cpu不足

        id:處於空閒的cpu百分比

        wa:系統等待IO的cpu時間百分比

        st:來自於一個虛擬機偷取的cpu時間的百分比

    • 查看額外

      • 查看全部cpu核信息 mpstat -P ALL 2
      • 每一個進程使用cpu的用量分解信息 pidstat -u 1 -p 進程編號
  3. 內存:free

    查看內存 free -m free -g

    pidstat -p 進程編號 -r 採樣間隔秒數

  4. 硬盤:df

    查看磁盤剩餘空間 df -h

  5. 磁盤IO:iostat

    • 磁盤I/O性能評估

      iostat -hdk 2 3

      • rkB/s每秒讀取數據kb;
      • wkB/s每秒讀寫數據量kb
      • svctm I/O請求的平均服務時間,單位毫秒;
      • await I/O請求的平均等待時間,單位毫秒;值越小,性能越好;
      • ==util== 一秒中又百分幾的時間用於I/O操做,接近100%時,表示磁盤帶寬跑滿,須要優化程序或加磁盤
      • rkB/s,wkB/s根據系統該應用不一樣回有不一樣的值,擔心規律遵循:長期、超大數據讀寫,確定不正常,須要優化程序讀取。
      • svctm的值與await的值很接近,表示幾乎沒有I/O等待,磁盤性能好,若是await的值遠高於svctm的值,則表示I/O隊列等待太長,須要優化程序或更換更快磁盤
    • pidstat -d 採樣間隔秒數 -p 進程號

  6. 網絡IO:ifstat

    ifstat l

十一、加入生產環境CPU佔用太高,談談分析思路和定位?

結合Linux和JDK命令一塊分析

11.1 案例步驟

  1. 先用top命令找出cpu佔比最高的

  2. ps -ef或者jps進一步定位,得知是一個怎樣的後臺程序惹事

    • jps -l
    • ps -ef|grep java|grep -v grep
  3. 定位到具體線程或者代碼

    • ps -mp 進程編號 -o Thread,tid,time

      定位到具體線程

      -m :顯示全部的線程

      -p pid 進程使用cpu的時間

      -o:該參數後是用戶自定義格式

  4. 將須要的線程ID轉換爲16禁止格式(英文小寫格式)

    • printf "%x\n" 線程ID
  5. jstack 進程Id|grep tid(16進制線程id小寫英文) -A60

    查看運行軌跡,堆棧異常信息

十二、對於JDK自帶的JVM監控和性能分析工具你用過那些?通常怎麼用?

相關文章
相關標籤/搜索