1. 程式人生 > 實用技巧 >JVM學習總結2

JVM學習總結2

GC基礎知識

垃圾

什麼是垃圾?

沒有引用的物件就是垃圾

如何找到垃圾?

  • 引用計數法(無法解決迴圈引用問題)
  • 根可達演算法(從執行緒棧變數、靜態變數、常量池、JNI指標開始)

GC演算法

標記清除

兩遍掃描,第一遍找出被引用的,第二遍找出沒有被引用的

效率偏低,容易產生碎片

拷貝

適用於存活物件較少的情況,只掃描一次,效率提高,沒有碎片

空間浪費,移動複製物件,需要調整物件引用

標記壓縮

不會產生碎片,方便物件分配,不會產生記憶體減半

掃描兩次,需要移動物件,效率偏低

堆記憶體邏輯分割槽

當物件剛建立的時候,是處於eden區的,經過一次垃圾回收之後進入survivor區,再經過一次垃圾回收之後進入第二個survivor區,當年齡夠了或者survivor放不下的時候就會進入tenured區

永久代,元資料區

jdk<1.8的時候,永久代存放class和字串常量,永久代開機指定大小,經常容易放不下東西導致記憶體溢位

jdk>=1.8之後,元資料區存放class,預設是所有記憶體

GC概念

MinorGC/YGC:年輕代空間耗盡時觸發

MajorGC/FullGC:在老年代無法繼續分配空間時觸發,新生代老年代同時進行回收

-X(非標引數)

-Xmn(memory new)

-Xms(整個堆空間(包括新生代、老年代)最小值)

-Xmx(整個堆空間(包括新生代、老年代)最大值 )

物件何時進入老年代

超過-XX:MaxTenuringThreshold指定次數(YGC)

  • PS 15
  • CMS 6
  • G1 15

總結

new一個物件的時候,看棧上面能不能分配,棧上面能夠分配那就棧上面分配。
棧上面分配不了,判斷是不是大物件。不是,就看看能不能在TLAB(每個執行緒在伊甸區的專屬堆空間)上面分配,不管能不能分配,都從Eden區分配。
如果是大物件,直接進入old區。
eden區記憶體不夠的時候,會發生YGC,當它發生的時候,會把eden區的內容拷貝到S1(第一個survivor區);第二次GC發生的時候,會把eden區和S1一起拷貝到S2(第二個survivor區)區;……;當物件的年齡夠了,就放到old區。
當old去記憶體不夠的時候,會發生FullGC,所有堆資料會全部重新整理。

常見的垃圾回收器

Serial + Serial Old(單執行緒)
Parallel Scavenge + Parallel Old(並行,多執行緒)
ParNew + CMS
G1
ZGC
Eplison

CMS

存在問題:

  1. 記憶體碎片
  2. 浮動垃圾

常見垃圾回收器組合引數設定

  • -XX:+UseSerialGC
  • -XX:+UseConc(urrent)MarkSweepGC = ParNew + CMS + Serial Old
  • -XX:+UseParallelGC
  • -XX:+UseG1GC
  • java +XX:+PrintCommandLineFlags -version,檢視特定垃圾回收器有哪些引數
  • java -Xmn10M -Xms40M -Xmx60M -XX:+PrintCommandLineFlags -XX:+PrintGC HelloGC
    PrintGCDetails PrintGCTimeStamps PrintGCCauses,列印GC引數

GC調優

基礎概念

  1. 吞吐量:使用者程式碼時間 / (使用者程式碼執行時間 + 垃圾回收時間)
  2. 響應時間:STW越短,響應時間越好

什麼是調優?

  1. 根據需求進行JVM規劃和預調優
  2. 優化執行JVM執行環境
  3. 解決JVM執行過程中出現的各種問題(OOM)

調優,從規劃開始

百萬併發是不可能的,淘寶2019雙十一才只有54萬併發,正常情況下1000併發已經很高了

  • 調優,從業務場景開始,沒有業務場景的調優都是耍流氓
  • 無監控(壓力測試,能看到結果),不調優
  • 步驟:
    1. 熟悉業務場景(沒有最好的垃圾回收器,只有最合適的垃圾回收器)
      1. 響應時間、停頓時間 [CMS G1 ZGC](需要給使用者做響應)
      2. 吞吐量 = 使用者時間 / (使用者時間 + GC時間)
    2. 選擇回收器組合
    3. 計算記憶體需求(經驗值1.5G 16G)
    4. 選定CPU(越高越好),因為執行緒多,垃圾回收速度快
    5. 設定年代大小,升級年齡
    6. 設定日誌引數
      1. -Xloggc:/opt/xx/logs/xxx-xxx-gc-%t.log -XX:+UseGCLogFileRotation -XX:NumberOfGCLogFiles=5 -XX:GCLogFileSize=20M -XX:+PrintGCDetails -XX:+PrintGCDateStamps -XX:+PrintGCCause
        列印的日誌加時間,迴圈列印,日誌個數5個,每個日誌檔案最大20M,列印GC細節,列印GC時間,列印GC原因
        tomcat有個catalina_options,配置在那裡面
      2. 或者每天產生一個日誌檔案
    7. 觀察日誌情況

優化環境

  1. 有一個50萬PV(Page View)的資料類網站(從磁碟提取文件到記憶體)原伺服器32位,1.5G的堆,使用者反饋網站比較緩慢,因此公司決定升級,新的伺服器為64位,16G的堆記憶體,結果使用者反饋卡頓十分嚴重,反而比以前效率更低了
    1. 為什麼慢?
      很多使用者瀏覽資料,很多資料load到記憶體,記憶體不足,頻繁GC,響應時間變慢
    2. 為什麼會更卡頓?
      記憶體越大,FGC時間越長
    3. 怎麼辦?
      PS -> PN + CMS 或者 G1
  2. 系統CPU經常100%,如何調優?(面試高頻)
    1. CPU100%那麼一定有執行緒在佔用系統資源,
    2. 找出哪個程序cpu高(top)
    3. 該程序中的哪個執行緒cpu高(top -Hp)
    4. 匯出該執行緒的堆疊(jstack)
    5. 查詢哪個方法(棧幀)消耗時間(jstack)
  3. 系統記憶體飆高,如何查詢問題?(面試高頻)
    1. 匯出堆記憶體(jmap)
    2. 分析(jhat jvisualvm mat jprofiler ...)
  4. 如何監控JVM
    1. jstat jvisualvm jprofiler arthas top ...

解決JVM執行中的問題

一個案例理解常用工具

  1. 測試程式碼(模擬風控):
    package com.mashibing.jvm.gc;
    
    import java.math.BigDecimal;
    import java.util.ArrayList;
    import java.util.Date;
    import java.util.List;
    import java.util.concurrent.ScheduledThreadPoolExecutor;
    import java.util.concurrent.ThreadPoolExecutor;
    import java.util.concurrent.TimeUnit;
    
    /**
    * 從資料庫中讀取信用資料,套用模型,並把結果進行記錄和傳輸
    */
    
    public class T15_FullGC_Problem01 {
    
       private static class CardInfo {
           BigDecimal price = new BigDecimal(0.0);
           String name = "張三";
           int age = 5;
           Date birthdate = new Date();
    
           public void m() {}
       }
    
       private static ScheduledThreadPoolExecutor executor = new ScheduledThreadPoolExecutor(50,
               new ThreadPoolExecutor.DiscardOldestPolicy());
    
       public static void main(String[] args) throws Exception {
           executor.setMaximumPoolSize(50);
    
           for (;;){
               modelFit();
               Thread.sleep(100);
           }
       }
    
       private static void modelFit(){
           List<CardInfo> taskList = getAllCardInfo();
           taskList.forEach(info -> {
               // do something
               executor.scheduleWithFixedDelay(() -> {
                   //do sth with info
                   info.m();
    
               }, 2, 3, TimeUnit.SECONDS);
           });
       }
    
       private static List<CardInfo> getAllCardInfo(){
           List<CardInfo> taskList = new ArrayList<>();
    
           for (int i = 0; i < 100; i++) {
               CardInfo ci = new CardInfo();
               taskList.add(ci);
           }
    
           return taskList;
       }
    }
  2. 一般是運維團隊首先收到報警資訊(CPU Memory)
  3. java -Xms200M -Xms -XX:+PrintGC 200M com.mashibing.jvm.gc.T15_FullGC_Problem01
  4. top命令觀察到問題:記憶體不斷增長 CPU佔用率居高不下
  5. top -Hp 觀察程序中執行緒的資源佔用
  6. jstack pid,可以看到裡面所有執行緒的資訊,執行緒的nid是十六進位制可以和top -Hp顯示的十進位制對應上
    重點關注:WAITING,BLOCKED
    eg.
    waiting on \<0x0000000088ca3310> (a java.lang.Object)
    假如有一個程序中100個執行緒,很多執行緒都在waiting on \,一定要找到是哪個執行緒持有這把鎖
    怎麼找?搜尋jstack dump的資訊,找\,看哪個執行緒持有這把鎖RUNNABLE
    作業:1:寫一個死鎖程式,用jstack觀察。2:寫一個程式,一個執行緒持有鎖不釋放,其他執行緒等待
  7. 為什麼阿里規範裡規定,執行緒的名稱(尤其是執行緒池)都要寫有意義的名稱
    怎麼樣自定義執行緒池裡的執行緒名稱?(自定義ThreadFactory)
  8. jps定位具體java程序
  9. jinfo pid(沒啥用)
  10. jstat -gc (jstat因為顯示比較雜,不建議使用,建議使用jconsole)動態觀察gc情況 / 閱讀GC日誌發現頻繁GC / arthas觀察 / jconsole(見下面jconsole遠端連線)/ jvisualVM / jprofiler(最好用)
    jstat -gc 4655 500:每個500個毫秒列印GC的情況
    如果面試官問你是怎麼定位OOM問題的?如果你回答用圖形介面(錯誤,因為JMX很佔用資源)
    1:已經上線的系統不用圖形介面用什麼?(cmdline arthas)
    2:圖形介面到底用在什麼地方?測試!測試的時候進行監控!(壓測觀察)
  11. jmap -histo 4655 | head -20,查詢有多少物件產生,線上檢視前20個記憶體佔用最大的物件
  12. jmap -dump:format=b,file=xxx pid /jmap -histo,手動匯出堆儲存檔案
    線上系統,記憶體特別大,jmap執行期間會對程序產生很大影響,甚至卡頓(電商不適合)
    1:設定了引數HeapDump,OOM的時候會自動產生堆儲存檔案
    2:很多伺服器備份(高可用),停掉這臺伺服器對其他伺服器不影響
    3:線上定位(arthas,一般小點公司用不到)
  13. java -Xms20M -Xmx20M -XX:+UseParallelGC -XX:+HeapDumpOnOutOfMemoryError com.mashibing.jvm.gc.T15_FullGC_Problem01
  14. 使用MAT / jhat(java heap analyse tool)進行dump檔案分析,其實這些都不好用,直接用visualVM就行了
    https://www.cnblogs.com/baihuitestsoftware/articles/6406271.html
  15. 找到程式碼的問題

jconsole遠端連線

  1. 程式啟動加入引數(JMX,Java Management X)

    java -Djava.rmi.server.hostname=192.168.17.11 -Dcom.sun.management.jmxremote -Dcom.sum.management.jmxremote.port=11111 -Dcom.sum.management.jmxremote.authenticate=false -Dcom.sun.management.jmxremote.ssl=false xxx

  2. 如果遭遇 Local host name unknown: XXX的錯誤,修改/etc/host檔案,把XXX加入進去

    192.168.17.11 basic

  3. 關閉linux防火牆(實戰中應該開啟對應埠)

    service iptables stop

    chkconfig iptables off # 永久關閉

  4. windows上開啟jconsole遠端連線192.168.17.11:11111

jvisualvm

  1. 新增JMX連線,寫上IP:埠

jprofiler(收費)

arthas(線上排查工具)

為什麼需要線上排查?

在生產上我們經常會碰到一些不好排查的問題,例如執行緒安全問題,用最簡單的threaddump或者heapdump不好查到問題原因。為了排查這些問題,有時我們會臨時加一些日誌,比如在一些關鍵的函式裡打印出入參,然後重新打包釋出,如果打了日誌還是沒找到問題,繼續加日誌,重新打包釋出。對於上線流程複雜而且稽核比較嚴的公司,從改程式碼到上線需要層層的流轉,會大大影響問題排查的進度。

下載、安裝

arthas,訪問github.com/alibaba/arthas

執行

在頻繁GC的程式同個機器上,執行java -jar arthas-boot.jar

輸入1

常用命令

jvm

jvm觀察jvm資訊

thread

thread定位執行緒問題,thread 54

dashboard

dashboard觀察系統情況

heapdump

heapdump匯出堆記憶體檔案,相當於jmap - dump:format=b,file=xxx pid / jmap -histo。也可以寫成heapdump /root/20200104.hprof

jad

jad反編譯
動態代理生成類的問題定位
第三方的類(觀察程式碼)
版本問題(確定自己最新提交的版本是不是被使用)

redefine

redefine熱替換
目前有些限制條件:只能改方法實現(方法已經執行完成),不能改方法名,不能改屬性
redefine /root/TT.class
m() -> mm(),你改了,別人還是呼叫m(),所以不可改

sc

search class

watch

watch method

沒有包含的功能

jmap

分析heapdump檔案

可以使用MAT(eclipse memory analyzer) / jhat / jvisualvm 進行dump檔案分析

jhat

jhat -J-mx512M 20200104.hprof

訪問web介面,拉到最後,找到對應的連結,可以用OQL查詢特定問題物件

select s from java.lang.String s

jvisualvm

裝入

生成dump檔案小技巧

java程式掛掉了,先別重啟,用jmap - dump:format=b,file=xxx pid / jmap -histo生成一個dump檔案

總結

CPU高 -> Top,JPS (檢視哪個程序出問題) -> jstack(看哪個執行緒) -> 如果是頻繁GC,就用jmap匯出來看

案例彙總

OOM產生的原因多種多樣,有些程式未必產生OOM,不斷FGC(CPU飆高,但記憶體回收特別少)(上面案例)

  1. 硬體升級系統反而卡頓的問題(見上)
  2. 執行緒池不當運用產生OOM問題(見上)
    不斷的往List里加物件(實在太LOW)
  3. smile jira問題
    實際系統不斷重啟
    解決問題 加記憶體 + 更換垃圾回收器G1
    真正問題在哪兒?不知道
  4. tomcat http-header-size過大問題,Http11OutputBuffer很大
  5. lambda表示式導致方法區溢位問題(MethodArea / <1.8 Perm >=1.8 Metaspace)
    因為lambda表示式其實是一個隨機的類,類會佔用方法區空間
    LambdaGC.java -XX:MaxMetaspaceSize=9M -XX:+PrintGCDetails
    java.lang.OutOfMemoryError: Compressed class space
  6. 直接記憶體溢位問題(少見)
    《深入理解Java虛擬機器》P59,使用Unsafe分配直接記憶體,或者使用NIO的問題
  7. 棧溢位問題(遞迴太深了)
    -Xss設定太小
  8. 比較一下這兩段程式的異同,分析哪一個是更優的寫法:
    Object o = null;
    for (int i = 0; i < 100; i++) {
       o = new Object();
    }
    
    for (int i = 0; i < 100; i++) {
       Object o = new Object();
    }

    第一種更好。因為第一個只有一個引用,第二個有很多個引用

  9. 重寫finalize引發頻繁GC
    小米雲,HBase同步系統,系統通過nginx訪問超時報警,最後排查,C++程式設計師重寫finalize引發頻發GC問題
    為什麼C++程式設計師會重寫finalize?(new delete)
    finalize耗時比較長(200ms)
  10. 如果有一個系統,記憶體一直消耗不超過10%,但是觀察GC日誌,發現FGC總是頻繁產生,會是什麼引起的?
    有人顯式呼叫了System.gc()(這個比較low)
  11. Distuptor有個可以設定鏈的長度,如果過大,然後物件大,消費完不主動釋放,會溢位
  12. 用jvm都會溢位,mycat用崩過,1.6.5某個臨時版本解析sql子查詢演算法有問題,9個exists的聯合sql就導致生成幾百萬的物件
  13. new大量執行緒,會產生native thread OOM,(low)應該用執行緒池
    解決方案:減少堆空間,預留更多記憶體產生native thread
    JVM記憶體佔實體記憶體比例50% - 80%

垃圾回收更多內容

CMS

  1. 初始標記
  2. 併發標記
  3. 重新標記
  4. 併發回收

初始標記

標記根物件

併發標記

重新標記

中間加了一個指標,原來的垃圾不再是垃圾了,所以需要重新標記。這個過程需要STW

有可能在finalize可以將垃圾變成非垃圾

併發回收

執行緒角度

併發清理的時候,有可能產生浮動垃圾

G1

參考:https://www.oracle.com/technical-resources/articles/java/g1gc.html

G1(garbage first)是一種服務端應用使用的垃圾收集器,目標是用在多核大記憶體的機器上,它在大多數情況下可以實現指定的GC暫定時間,同時還能保持較高的吞吐量

記憶體模型

特點

  • 併發收集
  • 壓縮空閒空間不會延長GC的暫停時間
  • 更易預測的GC暫停時間
  • 適用不需要實現很高的吞吐量的場景

三色標記和顏色指標

三色標記:標記物件處於垃圾回收的哪一個階段

顏色指標:標記物件指標有沒有變動過

G1的記憶體區域不是固定的E或者O

一塊記憶體有可能是伊甸區,也有可能是Old區GC演算法的基礎概念

  • Card Table
    由於做YGC時,需要掃描整個OLD區,效率非常低,所以JVM設計了CardTable,如果一個OLD區CardTable中有物件指向Y區,就將它設為Dirty,下次掃描時,只需要掃描Dirty Card Table
    在結構上,Card Table用BitMap來實現

G1演算法基礎概念

CSet

CSet = Collection Set

一組可被回收的分割槽的集合

在CSet中存活的資料會在GC過程中被移動到另一個可用分割槽,CSet中的分割槽可以來自Eden空間、survivor空間、或者老年代。CSet會佔用不到整個堆空間的1%大小

RSet(RememberedSet)

記錄了其他Region中的物件到本Region的引用

RSet的價值在於

使得垃圾收集器不需要掃描整個堆找到誰引用了當前分割槽中的物件,只需要掃描RSet即可

RSet 與 賦值的效率

  • 由於RSet的存在,那麼每次給物件賦引用的時候,就得做一些額外的操作
  • 指的是在RSet中做一些額外的記錄(在GC中被稱為寫屏障)
  • 這個寫屏障 不等於 記憶體屏障

為什麼使用G1

  • 追求吞吐量
    • 100 cpu
    • 99 app 1 GC
    • 吞吐量 = 99%
  • 追求響應時間
    • -XX:MaxGCPauseMillis 200
    • 對STW進行控制
  • 靈活
    • 分Region回收
    • 優先回收花費時間少、垃圾比例高的Region

每個Region有多大

  • headpRegion.cpp
  • 取值:1 2 4 8 16 32
  • 手工指定:-XX:G1HeapRegionSize

新老年代比例

5% - 60%

一般不用手工指定

也不要手工指定,因為這是G1預測停頓時間的基準

比方說基準是200ms,有一次YGC花了400ms,那麼G1就把Y區調小一點,這樣整理的就會快一點

GC何時觸發

  • YGC
    • Eden空間不足
    • 多執行緒並行執行
  • FGC
    • Old空間不足
    • System.gc()

G1會產生FGC嗎?產生怎麼辦?

會的,物件分配太快,分配不下的時候,就會產生

辦法:

  1. 擴記憶體
  2. 提高CPU效能(回收的快,業務邏輯產生物件的速度固定,垃圾回收越快,記憶體空間越大)
  3. 降低MixedGC觸發的閾值,讓MixedGC提早發生(預設是45%)

G1中的MixedGC

= CMS

XX:InitiatingHeapOccupacyPercent

預設45%

當O超過這個值是,啟動MixedGC

MixedGC的過程

  • 初始標記STW
  • 併發標記
  • 最終標記STW(重新標記)
  • 篩選回收STW(並行)

注意,G1在最後一步會合並內容,減少碎片

Full GC

java 10 以前是序列FullGC,之後是並行FullGC

降低MixedGC觸發的閾值,讓MixedGC提早發生(預設是45%)

阿里的多租戶JVM

  • 每租戶單空間
  • session based GC

併發標記演算法

三色標記法:

白色:未被標記的物件

灰色:自身被標記,成員變數未被標記

黑色:自身和成員變數均已標記完成

漏標

漏標是指,本來是live object,但是由於沒有遍歷到,被當做garbage回收掉了

併發標記階段,黑色物件指向白色物件,與此同時灰色物件刪除指向,然後D就漏標了

解決方案:跟蹤A指向D的增加,跟蹤B指向D的消失

這兩種分別叫做:
incremental update(增量更新,關注引用的增加,CMS用的就是這個演算法)
SATB(snapshot at the beginning)(關注引用的刪除)(當B->D消失時,要把這個引用推到GC的堆疊,保證D還能被GC掃描到,G1用的就是這個演算法)

為什麼CMS和G1用不同的演算法?

CMS把黑色標為灰色,意味著下次要把已經標記過的物件重新再掃描一次,效率太低了

所以G1用SATB,另外G1用了Remember Set,它能知道哪些指向自己的指標現在沒有了

灰色 -> 白色 引用消失時,如果沒有黑色指向白色引用會被push到堆疊
下次掃描時拿到這個引用,由於有RSet的存在,不需要掃描整個堆去查詢指向白色的引用,效率比較高
SATB配合RSet,渾然天成

漏標的會在Remark階段重新標記

JVM調優

CMS

CMS的問題

  1. Memory Fragmentation

    -XX:+UseCMSCompactAtFullCollection
    -XX:CMSFullGCsBeforeCompaction 預設為0 指的是經過多少次FGC才進行壓縮

  2. Floating Garbage

    Concurrent Mode Failure
    產生:if the concurrent collector is unable to finish reclaiming the unreachable objects before the tenured generation fills up, or if an allocation cannot be satisfiedwith the available free space blocks in the tenured generation, then theapplication is paused and the collection is completed with all the applicationthreads stopped

    解決方案:降低觸發CMS的閾值

    PromotionFailed

    解決方案類似,保持老年代有足夠的空間

    –XX:CMSInitiatingOccupancyFraction 92% 可以降低這個值,讓CMS保持老年代足夠的空間

CMS日誌分析

執行命令:java -Xms20M -Xmx20M -XX:+PrintGCDetails -XX:+UseConcMarkSweepGC com.mashibing.jvm.gc.T15_FullGC_Problem01

[GC (Allocation Failure) [ParNew: 6144K->640K(6144K), 0.0265885 secs] 6585K->2770K(19840K), 0.0268035 secs] [Times: user=0.02 sys=0.00, real=0.02 secs]

ParNew:年輕代收集器

6144->640:收集前後的對比

(6144):整個年輕代容量

6585 -> 2770:整個堆的情況

(19840):整個堆大小

[GC (CMS Initial Mark) [1 CMS-initial-mark: 8511K(13696K)] 9866K(19840K), 0.0040321 secs] [Times: user=0.01 sys=0.00, real=0.00 secs]
    //8511 (13696) : 老年代使用(最大)
    //9866 (19840) : 整個堆使用(最大)
[CMS-concurrent-mark-start]
[CMS-concurrent-mark: 0.018/0.018 secs] [Times: user=0.01 sys=0.00, real=0.02 secs]
    //這裡的時間意義不大,因為是併發執行
[CMS-concurrent-preclean-start]
[CMS-concurrent-preclean: 0.000/0.000 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
    //標記Card為Dirty,也稱為Card Marking
[GC (CMS Final Remark) [YG occupancy: 1597 K (6144 K)][Rescan (parallel) , 0.0008396 secs][weak refs processing, 0.0000138 secs][class unloading, 0.0005404 secs][scrub symbol table, 0.0006169 secs][scrub string table, 0.0004903 secs][1 CMS-remark: 8511K(13696K)] 10108K(19840K), 0.0039567 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
    //STW階段,YG occupancy:年輕代佔用及容量
    //[Rescan (parallel):STW下的存活物件標記
    //weak refs processing: 弱引用處理
    //class unloading: 解除安裝用不到的class
    //scrub symbol(string) table:
        //cleaning up symbol and string tables which hold class-level metadata and
        //internalized string respectively
    //CMS-remark: 8511K(13696K): 階段過後的老年代佔用及容量
    //10108K(19840K): 階段過後的堆佔用及容量

[CMS-concurrent-sweep-start]
[CMS-concurrent-sweep: 0.005/0.005 secs] [Times: user=0.00 sys=0.00, real=0.01 secs]
    //標記已經完成,進行併發清理
[CMS-concurrent-reset-start]
[CMS-concurrent-reset: 0.000/0.000 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
    //重置內部結構,為下次GC做準備

G1

  1. https://www.oracle.com/technical-resources/articles/java/g1gc.html

G1日誌詳解

[GC pause (G1 Evacuation Pause) (young) (initial-mark), 0.0015790 secs]
//young -> 年輕代 Evacuation-> 複製存活物件
//initial-mark 混合回收的階段,這裡是YGC混合老年代回收
   [Parallel Time: 1.5 ms, GC Workers: 1] //一個GC執行緒
      [GC Worker Start (ms):  92635.7]
      [Ext Root Scanning (ms):  1.1]
      [Update RS (ms):  0.0]
         [Processed Buffers:  1]
      [Scan RS (ms):  0.0]
      [Code Root Scanning (ms):  0.0]
      [Object Copy (ms):  0.1]
      [Termination (ms):  0.0]
         [Termination Attempts:  1]
      [GC Worker Other (ms):  0.0]
      [GC Worker Total (ms):  1.2]
      [GC Worker End (ms):  92636.9]
   [Code Root Fixup: 0.0 ms]
   [Code Root Purge: 0.0 ms]
   [Clear CT: 0.0 ms]
   [Other: 0.1 ms]
      [Choose CSet: 0.0 ms]
      [Ref Proc: 0.0 ms]
      [Ref Enq: 0.0 ms]
      [Redirty Cards: 0.0 ms]
      [Humongous Register: 0.0 ms]
      [Humongous Reclaim: 0.0 ms]
      [Free CSet: 0.0 ms]
   [Eden: 0.0B(1024.0K)->0.0B(1024.0K) Survivors: 0.0B->0.0B Heap: 18.8M(20.0M)->18.8M(20.0M)]
[Times: user=0.00 sys=0.00, real=0.00 secs]
//以下是混合回收其他階段
[GC concurrent-root-region-scan-start]
[GC concurrent-root-region-scan-end, 0.0000078 secs]
[GC concurrent-mark-start]
//無法evacuation,進行FGC
[Full GC (Allocation Failure)  18M->18M(20M), 0.0719656 secs]
   [Eden: 0.0B(1024.0K)->0.0B(1024.0K) Survivors: 0.0B->0.0B Heap: 18.8M(20.0M)->18.8M(20.0M)], [Metaspace: 38
76K->3876K(1056768K)] [Times: user=0.07 sys=0.00, real=0.07 secs]

GC常用引數

  • -Xmn -Xms -Xmx -Xss
    年輕代 最小堆 最大堆 棧空間
  • -XX:+UseTLAB
    使用TLAB,預設開啟
  • -XX:+PrintTLAB
    列印TLAB的使用情況
  • -XX:TLABSize
    設定TLAB大小
  • -XX:+DisableExplictGC
    System.gc()不管用 ,FGC
  • -XX:+PrintGC
  • -XX:+PrintGCDetails
  • -XX:+PrintHeapAtGC
  • -XX:+PrintGCTimeStamps
  • -XX:+PrintGCApplicationConcurrentTime (低)
    列印應用程式時間
  • -XX:+PrintGCApplicationStoppedTime (低)
    列印暫停時長
  • -XX:+PrintReferenceGC (重要性低)
    記錄回收了多少種不同引用型別的引用
  • -verbose:class
    類載入詳細過程
  • -XX:+PrintVMOptions
  • -XX:+PrintFlagsFinal -XX:+PrintFlagsInitial
    必須會用
  • -Xloggc:opt/log/gc.log
  • -XX:MaxTenuringThreshold
    升代年齡,最大值15
  • 鎖自旋次數 -XX:PreBlockSpin 熱點程式碼檢測引數-XX:CompileThreshold 逃逸分析 標量替換 ...
    這些不建議設定

Parallel常用引數

  • -XX:SurvivorRatio
  • -XX:PreTenureSizeThreshold
    大物件到底多大
  • -XX:MaxTenuringThreshold
    物件升入老年代的年齡
  • -XX:+ParallelGCThreads
    並行收集器的執行緒數,同樣適用於CMS,一般設為和CPU核數相同
  • -XX:+UseAdaptiveSizePolicy
    自動選擇各區大小比例

CMS常用引數

  • -XX:+UseConcMarkSweepGC
  • -XX:ParallelCMSThreads
    CMS執行緒數量
  • -XX:CMSInitiatingOccupancyFraction
    使用多少比例的老年代後開始CMS收集,預設是68%(近似值),如果頻繁發生SerialOld卡頓,應該調小,(頻繁CMS回收)
  • -XX:+UseCMSCompactAtFullCollection
    在FGC時進行壓縮
  • -XX:CMSFullGCsBeforeCompaction
    多少次FGC之後進行壓縮
  • -XX:+CMSClassUnloadingEnabled
  • -XX:CMSInitiatingPermOccupancyFraction
    達到什麼比例時進行Perm回收
  • GCTimeRatio
    設定GC時間佔用程式執行時間的百分比
  • -XX:MaxGCPauseMillis
    停頓時間,是一個建議時間,GC會嘗試用各種手段達到這個時間,比如減小年輕代

G1常用引數

  • -XX:+UseG1GC
  • -XX:MaxGCPauseMillis
    建議值,G1會嘗試調整Young區的塊數來達到這個值
  • -XX:GCPauseIntervalMillis
    ?GC的間隔時間
  • -XX:+G1HeapRegionSize
    分割槽大小,建議逐漸增大該值,1 2 4 8 16 32。
    隨著size增加,垃圾的存活時間更長,GC間隔更長,但每次GC的時間也會更長
    ZGC做了改進(動態區塊大小)
  • G1NewSizePercent
    新生代最小比例,預設為5%
  • G1MaxNewSizePercent
    新生代最大比例,預設為60%
  • GCTimeRatio
    GC時間建議比例,G1會根據這個值調整堆空間
  • ConcGCThreads
    執行緒數量
  • InitiatingHeapOccupancyPercent
    啟動G1的堆空間佔用比例

作業

  1. -XX:MaxTenuringThreshold控制的是什麼?(A)
    A: 物件升入老年代的年齡
    B: 老年代觸發FGC時的記憶體垃圾比例
  2. 生產環境中,傾向於將最大堆記憶體和最小堆記憶體設定為:(為什麼?)(A,防止記憶體彈性收縮)
    A: 相同 B:不同
  3. JDK1.8預設的垃圾回收器是:(C)
    A: ParNew + CMS
    B: G1
    C: PS + ParallelOld
    D: 以上都不是
  4. 什麼是響應時間優先?(STW時間要儘可能短)
  5. 什麼是吞吐量優先?(垃圾回收總時間佔比儘可能少)
  6. ParNew和PS的區別是什麼?(ParNew和CMS配合使用,PS和PO配合使用)
  7. ParNew和ParallelOld的區別是什麼?(年代不同,演算法不同)(前者用於新生代,後者用於老年代)
  8. 長時間計算的場景應該選擇:A:停頓時間 B: 吞吐量(B)
  9. 大規模電商網站應該選擇:A:停頓時間 B: 吞吐量(A)
  10. HotSpot的垃圾收集器最常用有哪些?(Serial+Serial Old,PS+PO,ParNew+CMS,G1,ZGC)
  11. 常見的HotSpot垃圾收集器組合有哪些?(Serial+Serial Old,PS+PO,ParNew+CMS)
  12. JDK1.7 1.8 1.9的預設垃圾回收器是什麼?如何檢視?(PS+PO,使用命令java -XX:+PrintCommandLineFlags -version
  13. 所謂調優,到底是在調什麼?(1. 根據需求進行JVM規劃和預調優,2. 優化執行JVM執行環境,3. 解決JVM執行過程中出現的各種問題(OOM))
  14. 如果採用PS + ParrallelOld組合,怎麼做才能讓系統基本不產生FGC(增加年輕代的大小?)
  15. 如果採用ParNew + CMS組合,怎樣做才能夠讓系統基本不產生FGC(減少CMS回收閾值)

    1.加大JVM記憶體

    2.加大Young的比例

    3.提高Y-O的年齡

    4.提高S區比例

    5.避免程式碼記憶體洩漏

  16. G1是否分代?G1垃圾回收器會產生FGC嗎?(邏輯分代,會)
  17. 如果G1產生FGC,你應該做什麼?
    1. 擴記憶體
    2. 提高CPU效能(回收的快,業務邏輯產生物件的速度固定,垃圾回收越快,記憶體空間越大)
    3. 降低MixedGC觸發的閾值,讓MixedGC提早發生(預設是45%)
  18. 問:生產環境中能夠隨隨便便的dump嗎?
    小堆影響不大,大堆會有服務暫停或卡頓(加live可以緩解),dump前會有FGC
  19. 問:常見的OOM問題有哪些?
    棧 堆 MethodArea 直接記憶體

程式,程序,執行緒,纖程(協程)

執行緒和纖程的本質區別:執行緒是核心級別,纖程是使用者級別

Quaser,java的纖程庫

程式啟動前需要加VM引數:-javaagent:quaser.jar

執行緒和纖程

一個重,一個輕

一個在核心,一個在使用者

一個少,一個多

為什麼?因為需要切換快