死磕Java虛擬機-性能調優理論課

一.  如何找到一個垃圾?
java


1)  引用計數算法:給對象添加一個引用計數器,有一次引用,計數器值就加1;當引用失效時,計數器值就減1。不少流程的編程語言例如Python都使用這種方法管理內存,可是主流的Java虛擬機沒有選用它,主要緣由是它很難解決對象之間相互循環引用的問題。面試


2)  根可達性分析算法:由於引用計數算法沒法解決對象之間相互循環引用的問題,繼而引出了這個算法。思想是以GC Roots做爲起始點開始向下搜索,所走過的路徑成爲引用鏈,當一個對象到GC Roots沒有任何引用鏈時,則這個對象是不可用的,就是垃圾。如圖所示:算法



二.  找到一個垃圾後,如何清除它?編程


1)  Mark-Sweep(標記清除):先標記出可回收的對象,而後清除。以下黑色部分爲可回收對象,灰色部分爲存活對象,綠色部分爲未使用對象。服務器

它的主要不足有兩個:
微信

1. 標記和清除兩個過程的效率都不高
併發

2. 另外一個是空間的問題,標記清除以後產生了大量不連續的內存碎片,碎片會致使之後須要分配較大對象時,沒法找到足夠的連續內存,繼而提早觸發另外一次垃圾收集動做。app


2)  Copying(拷貝):將內存劃分爲兩塊,將存活對象所有copy到下面的區域,而後把上面的所有清除。編程語言

新生代中的Survivor1和Survivor2就是這樣的。學習

缺點:浪費內存


3)  Mark-Compact(標記壓縮或者標記整理):既不想碎片化,又不想浪費內存,就先將回收的對象標記起來,而後一邊回收,一邊把存活對象向一端移動。

缺點:效率偏低


三. 內存分配和回收的過程是什麼樣子的



內存分配與回收策略以下圖:



名詞解釋:

YGC ==Young GC == Minor GC 新生代回收

Major GC 老年代回收

Full GC 新生代和老年代一塊回收


圖示解釋:

1) new出一個對象,首先嚐試在棧上分配,若是能直接分配下,就在棧上分配。對象在棧裏一旦pop,對象就沒了。(棧上分配好處:不須要GC介入,對象用完就能夠消失,可是棧的空間很是小)


2) 若是棧上分配不下,而且對象比較大,會直接進入老年代,經歷Major GC/Full GC的回收,而後消亡。


3) 若是對象不夠大,先在線程本地分配 (TLAB: Thread Local Allocation Buffer),若是TLAB滿了,直接進入Eden,無論進不進入TLAB,最終都會進入Eden區域,進入Eden區域會競爭資源,出現線程同步,特別消耗資源,所以Hotspot作了優化,Eden區域爲每一個線程都分配了一小塊區域,這樣就不會每一個線程都去搶奪資源。


4) 在Eden區域,對象若是能被GC清除,直接就消亡了


5) 在Eden區域,若是回收不掉,進入Survivor1區域,而後再次回收,年齡加1,若是年齡到了(CMS默認是6歲,其餘默認都是15歲),而後進入老年代,若是年齡沒到,進入Survivor2區域,而後再次回收,循環往復,Survivor1和Survivor2還會來回替換(由於有個copy垃圾回收算法),直到進入老年代。


四.  垃圾收集器(10種)


前面六個是分代模型,後面四個是不分代模型。


1. 垃圾收集器的發展路線,是隨着內存愈來愈大的過程而演進的。

    從分代算法演化到不分代算法

    Serial算法 幾十兆

    Parallel算法 幾個G

    CMS 幾十個G  承上啓下,開始併發回收

    -三色標記-


2. JDK誕生,Serial追隨 提升效率,誕生了PS,爲了配合CMS,誕生了PN,CMS是1.4版本後期引入,CMS是里程碑式的GC,它開啓了併發回收的過程,可是CMS毛病較多,併發垃圾回收的緣由是由於沒法忍受Serial回收的STW。


3. Serial 收集器  年輕代 串行回收

特色:在回收垃圾時,必須暫停其餘全部的工做線程,知道它收集結束


4. PS 年輕代 並行回收


5. ParNew 年輕代 配合CMS的並行回收,它和Parallel Scavenge同樣,區別就是它配合CMS使用。


6. Serial Old 收集器


7. Parallel Old


8. CMS 全稱 ConcurrentMarkSweep 老年代 併發的,垃圾回收和應用程序同時進行,下降STW的時間(200ms),CMS問題不少,因此沒有一個版本默認是CMS,只能手動設定。CMS既然是MarkSweep,就必定會有碎片化的問題,碎片達到必定的程度,CMS老年代分配對象分配不下的時候,使用Serial Old進行老年代回收。(面試重災區)

主要有四個過程:

初始標記、併發標記、從新標記、併發清理



CMS的缺點是:產生了浮動垃圾,而且使用Serial Old來清理整個老年代,這是CMS設計的缺陷;可是若是CMS作好調優,支持的內存要比Parallel Old大的多。


想象一下:

PS + PO -> 加內存 換垃圾回收器 -> PN + CMS + Serial Old (幾個小時-幾天的STW) ,幾十個G 的內存,單線程的回收 -> G1 + Full GC  幾十個G -> 上T內存的服務器 ZGC 


CMS併發標記採用的是: 三色標記法 + Incremental Update


9.  G1 垃圾回收器 (200ms - 10 ms)

算法:三色標記 + SATB

因爲愈來愈多的內存須要回收,必然會產生STW,因此G1應運而生。

邏輯分代,物理不分代。


10.  ZGC (10ms - 1ms )  PK C++

算法:ColoredPointers + LoadBarrier


11.  Shenandoah

算法:ColorPointers + WriteBarrier


CMS中新生代的默認年齡是6,PS/PO中新生代的默認年齡是15,進入老年代,能夠經過參數:-XX:MaxTenuringThreshold配置


jdk1.0自帶的Serial和Serial Old,如今用的最多的是Parallel Scavenge和Parallel Old,調優用的是ParNew和CMS,jdk1.8用G1也沒有問題


上圖:前面六種,通常都是新生代和老年代配合使用。ParNew和CMS,Serial和Serial Old,Parallel Scavenge和Parallel Old。


jdk1.8默認是Parallel Scavenge和Parallel Old,不論是單線程Serial仍是並行Parallel,只要人多,都會出現問題,因此就有了承前啓後的ParNew和CMS。

本文分享自微信公衆號 - Java學習進階手冊(javastudyup)。
若有侵權,請聯繫 support@oschina.cn 刪除。
本文參與「OSC源創計劃」,歡迎正在閱讀的你也加入,一塊兒分享。

相關文章
相關標籤/搜索