天天看點

JVM GC算法和垃圾收集器記憶體配置設定政策Gc相關參數Java虛拟機調優參數Minor GC日志檢視FullGC日志檢視jVisualVM圖形化使用性能調優實戰 

1.判斷對象是否是被引用的兩種方法

引用計數法,不能回收循環引用的對象,JDK1.3之後已經不用。

根搜尋算法,也就是GC root 算法,棧中的局部變量,還有(元區間)方法區,本地方法棧引用的對象。

為什麼jvm不直接采用gcroot去回收整個堆 效率不高,我們程式開發 我們的對象99.99% 對象 基本存活不到3秒鐘

2.複制算法

主要是用在新生代,新生代和老年代預設比例為1:2,然後新生代裡面又分為eden區和S0區,S1區也叫from和to區,這3個預設比例是8:1:1,當Eden區快滿的時候會觸發一次minor gc,然後存活下來的會年齡加一然後,S0,S1區會複制移動一直空出一塊。

3.标記壓縮(标記整理)算法。

發生在老年代,就是把不可達的對象移動到連續的一塊再回收,優點不會産生磁盤碎片,缺點需要移動對象需要占用更多的CPU資源和STW的時間會久一些

4.标記清除算法。

發生在老年代,就是把不可達的對象直接标記删除,優點會快一些,缺點會産生磁盤碎片。

二,垃圾收集器

垃圾收集器,是jvm對垃圾收集的工具,裡面用的都是上面的垃圾收集算法,一般都是上面的分代算法

1.Serial收集器

則是發展最悠久的垃圾收集器。在jdk1.3的時候隻能用我們serial垃圾回收器。

他是一個單線程的垃圾回收器。用在我們的新生代複制算法

在桌面應用比較多(單線程伺服器上,堆記憶體比較小的應用使用效率比較高)

JVM GC算法和垃圾收集器記憶體配置設定政策Gc相關參數Java虛拟機調優參數Minor GC日志檢視FullGC日志檢視jVisualVM圖形化使用性能調優實戰 

當我們gc執行時候會暫停我們的所有的線程這個步驟簡稱STW (Stop The World)

2.Parnew收集器

在清理時候采用我們的parnew 采用我們的 多線收集也是在新生代複制算法

JVM GC算法和垃圾收集器記憶體配置設定政策Gc相關參數Java虛拟機調優參數Minor GC日志檢視FullGC日志檢視jVisualVM圖形化使用性能調優實戰 

3.Parallel Scavenge收集器

他也是采用我們的複制算法

多線程收集

達到到可以控制的吞吐量    使用者代碼時間/(使用者代碼時間+gc暫停時間)

-XX:MaxGCPauseMillis 垃圾回收器最大停頓時間

-XX:GCTimeRatio 吞吐量大小  (0,100) 預設最大99

4.CMS收集器(concurrent mark sweep)

工作過程:

初始标記:使用可達性分析法标記 标記gcroot直接關聯上的對象。

并發标記:由前階段标記過的對象出發,所有可到達的對象都在本階段中标記。

并發預清理 :并發預清理階段仍然是并發的。在這個階段,虛拟機查找在執行并發标記階段新進入老年代的對象(可能會有一些對象從新生代晉升到老年代, 或者有一些對象被配置設定到老年代)。通過重新掃描,減少下一個階段"重新标記"的工作,因為下一個階段會Stop The World。

重新标記:标記那些因為使用者程式繼續運作産生的新生帶跨帶引用的垃圾對象

并發清理:清理所有的垃圾對象。

并發重置:将原本的标記對象重置

JVM GC算法和垃圾收集器記憶體配置設定政策Gc相關參數Java虛拟機調優參數Minor GC日志檢視FullGC日志檢視jVisualVM圖形化使用性能調優實戰 

優點:

并發收集

低停頓

缺點:

占用大量的cpu資源

無法處理浮動垃圾

會産生碎片化

用在老年代中 cms隻能和serial或者parnew使用。不能使用parallel

如何確定新生代對象被老年代對象引用的時候不被gc?

老年代存活對象多時,每次minor gc查詢老年代所有對象影響gc效率(因為gc stop-the-world),是以在老年代有一個write barrier(寫屏障)來管理的card table(卡表),card table存放了所有老年代對象對新生代對象的引用。

是以每次minor gc通過查詢card table來避免查詢整個老年代,以此來提高gc性能

記憶體配置設定政策

建立的對象會優先配置設定到eden區域

大對象直接配置設定到老年代

有對應的參數配置設定多大的對象會直接進入我們的老年代 使用 要指定的收集器才有效

-XX:PretenureSizeThreshold

長期存活的的對象配置設定到老年代

空間配置設定擔保

當對象生成在EDEN區失敗時,出發一次YGC,先掃描EDEN區中的存活對象,進入S0區,S0放不下的進入OLD區,再掃描S1區,若存活次數超過閥值則進入OLD區,其它進入S0區,然後S0和S1交換一次。

那麼當發生YGC時,JVM會首先檢查老年代最大的可用連續空間是否大于新生代所有對象的總和,如果大于,那麼這次YGC是安全的,如果不大于的話,JVM就需要判斷HandlePromotionFailure是否允許空間配置設定擔保。

允許配置設定擔保:

JVM繼續檢查老年代最大的可用連續空間是否大于曆次晉升到老年代的對象的平均大小,如果大于,則正常進行一次YGC,盡管有風險(因為判斷的是平均大小,有可能這次的晉升對象比平均值大很多);

如果小于,或者HandlePromotionFailure設定不允許空間配置設定擔保,這時要進行一次FGC。

新生代采用的是複制收集算法,S0和S1始終隻是用其中一塊記憶體區,當出現YGC後大部分對象仍然存活的話,就需要老年代進行配置設定擔保,把survior區無法容納的對象直接晉升到老年代。

那麼這種空間配置設定擔保的前提是老年代還有容納的空間,一共有多少對象會活下來,在實際完成記憶體回收之前是無法明确知道的,是以隻好取之前每次回收晉升到老年代對象容量的平均值大小作為經驗值,與老年代的剩餘空間比較,決定是否進行FGC來讓老年代騰出更多空間。

動态對象年齡判斷

當 Survivor 空間中相同年齡所有對象的大小總和大于 Survivor 空間的一半,年齡大于或等于該年齡的對象就可以直接進入老年代,而不需要達到預設的分代年齡

Gc相關參數

控制台列印gc日志

-verbose:gc

-XX:+PrintGCDetails

-XX:+PrintHeapAtGC(詳細的gc資訊)

輸出gc日志到指定檔案

 -Xloggc:

(例如:  -Xloggc:D:\logs\gc.log)

Gc日志分塊

-XX:-UseGCLogFileRotation

-XX:GCLogFileSize = 8M

指定最小堆記憶體

-Xms

(例如-Xms20M指定最小堆記憶體為20M)

指定最大堆記憶體

-Xmx

(例如-Xms20M指定最大堆記憶體為20M)

指定新生代記憶體大小

-Xmn

(例如-Xmn10M指定新生代記憶體為10M)

指定eden區在新生代的占比

-XX:SurvivorRatio=8

(eden比S0,S1區比例為8:1:1)

1.8元空間設定大小

-XX:MetaspaceSize

初始空間大小,達到該值就會觸發垃圾收集進行類型解除安裝,同時GC會對該值進行調整:如果釋放了大量的空間,就适當降低該值;如果釋放了很少的空間,那麼在不超過MaxMetaspaceSize時,适當提高該值。

-XX:MaxMetaspaceSize

最大空間,預設是沒有限制的。

指定建立的對象超過多少會直接建立在老年代

此參數隻能在是有serial收集器,和我們的parnew收集器才有效

-XX:PretenureSizeThreshold

(比如 -XX:PretenureSizeThreshold=1M)

指定多大年齡的對象進入老年代

-XX:MaxTenuringThreshold

(比如 -XX:MaxTenuringThreshold=15 預設也是15次)

記憶體溢出時候列印堆記憶體快照

-XX:+HeapDumpOnOutOfMemoryError

該配置會把快照儲存在使用者目錄或者tomcat目錄下,也可以通過 -XX:HeapDumpPath=/tmp/heapdump.dump來顯示指定路徑

使用serialGC收集器

-XX:+UseSerialGC  

使用ParNew收集器

-XX:+UseParNewGC

使用cms收集器

-XX:+UseConcMarkSweepGC

該标志首先是激活CMS收集器。預設HotSpot JVM使用的是并行收集器。

啟動CMS多線程執行

-XX:+CMSConcurrentMTEnabled

指定CMS啟動線程個數

 -XX:ConcGCThreads

标志-XX:ConcGCThreads=<value>(例如:-XX:ConcGCThreads=4)

指定老年代記憶體達到多少的百分比進行垃圾收集

-XX:CMSInitiatingOccupancyFraction=70 和

-XX:+UseCMSInitiatingOccupancyOnly

執行多少次fullgc後執行一次full gc的标記壓縮算法

預設參數場景是:-XX:+UseCMSCompactAtFullCollection -XX:CMSFullGCsBeforeCompaction=0。這意味着每次full gc(标記清除)後,都會壓縮

開啟在CMS重新标記階段之前的清除minor gc

-XX:+CMSScavengeBeforeRemark

G1收集器

垃圾優先(G1)垃圾收集選項

選項和預設值 描述
-XX:+ UseG1GC 使用垃圾優先(G1)收集器
-XX:MaxGCPauseMillis = n 設定最大GC暫停時間的目标。這是一個軟目标,JVM将盡最大的努力來實作它。
-XX:InitiatingHeapOccupancyPercent = n 啟動并發GC周期的(整個)堆占用百分比。GC使用它來觸發GC,該GC基于整個堆的占用來觸發并發GC周期,而不僅僅是世代之一(例如,G1)。值為0表示“進行恒定的GC循環”。預設值為45。
-XX:NewRatio = n 新舊大小比例。預設值為2。
-XX:SurvivorRatio = n 伊甸園/幸存者空間大小之比。預設值為8。
-XX:MaxTenuringThreshold = n 任職期限的最大值。預設值為15。
-XX:ParallelGCThreads = n 設定在垃圾回收器的并行階段使用的線程數。預設值随運作JVM的平台而異。
-XX:ConcGCThreads = n 并發垃圾收集器将使用的線程數。預設值随運作JVM的平台而異。
-XX:G1ReservePercent = n 設定保留為虛假上限的堆數量,以減少更新失敗的可能性。預設值為10。
-XX:G1HeapRegionSize = n 使用G1,Java堆可細分為大小一緻的區域。這将設定各個細分的大小。該參數的預設值是根據堆大小按人機工程學确定的。最小值為1Mb,最大值為32Mb。

Region之H區域

Humongous區是JDK8新增的一個針對于大對象的特殊區域,其隸屬于老年代,當配置設定的對象大小大于Region大小的50%時,這個對象會被認為是大對象,将會配置設定到Humongous區,對于一個Humongous區也無法容納的對象,G1會尋找一個連續的HRegion來存放該對象,如果找不到會啟動fullgc回收來達到目的,并且因為Humongous區是屬于老年代的,大對象過多會大大增加fullgc的頻率,是以在程式中一定要避免出現大對象;

Rset(Remembered set)

全稱是Remembered Set,是輔助GC過程的一種結構,典型的空間換時間工具,和Card Table有些類似。還有一種資料結構也是輔助GC的:Collection Set(CSet),它記錄了GC要收集的Region集合,集合裡的Region可以是任意年代的。在GC的時候,對于old->young和old->old的跨代對象引用,隻要掃描對應的CSet中的RSet即可。

記入其他region引入目前region的對象

Cset

代表我們本次gc要回收region的集合

YGC

清理我們的S0區域,然後生成一個新的S1區,其他的區域就會變成空白的。

Mix GC

  1. 初始标記:标記GCroot對象,還會标記我們的region(塊)
  2. 标記Region:更具我們rset 找到對應和rootRegion相關的region塊進行辨別
  3. 并發标記:周遊所有的rootRegion引用的所有的區标記我們所有的可達對象
  4. 重新标記:通CMS一樣,隻不過用的是SATB
  5. 清理:清理采用的是複制算法。這裡清理隻會選取垃圾較多的region

Java虛拟機調優參數

jmap指令

jmap -heap pid列印目前記憶體存儲快照

jmap -dump:format=b,file=#輸出dump位址  pid

例如:jmap -dump:format=b,file=d:\a.dump 17700

jhat 指令檢視dump檔案

例如:jhat d:\a.bin 17700

jstat指令

jstat -gcutil 17700

Jstat -gcutil #程序id  #間隔時間ms  #監聽次數

jstack指令

生産目前的線程快照

例如 jstack pid

查找最消耗cpu的java線程

jps

找到java程序PID

指令:ps -mp pid -o THREAD,tid,time  或者  ps -Lfp pid

 通過%CPU和 TIME,判斷占用的線程TID

找到TID,轉換成16進制,然後在上面(jstack 程序id > ps.txt)導出的檔案中搜尋,就可以定位到具體的線程,類。

Minor GC日志檢視

FullGC日志檢視

jVisualVM圖形化使用

引入我們的dump檔案解析

性能調優實戰

案例一:内部系統主要是一個跑批系統

環境:64G jdk7 CentOs

問題:經常由使用者反映長時間的出現卡頓

處理思路

  1. 優化sql :發現是有些時候會比較慢,并不是一直在用
  2. 監控CPU比較正常
  3. 經常會發生full gc 20-30s的fullgc

記憶體也比較夠用。堆記憶體設定比較大

總結經驗:

更改堆記憶體大小

java -jar -verbose:gc -XX:+PrintGCDetails -XX:+UseConcMarkSweepGC -XX:+CMSConcurrentMTEnabled -XX:CMSFullGCsBeforeCompaction=5 -XX:PretenureSizeThreshold=3M -XX:+CMSConcurrentMTEnabled -Xms10240M -Xmx10240M apollo-test-0.0.1-SNAPSHOT.jar

jvm