GC格式、常規優化方式

垃圾回收機制的分類

CMS

CMS堆內存和以網的垃圾收集器一樣,分爲新生代和老年代,新生代和老年代是物理隔離的。

[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-RMEGAHNR-1592646953798)(https://cdn.nlark.com/yuque/0/2020/webp/99448/1592532635546-b62d959d-47cf-4505-b51d-0cd1b66759d3.webp)]

G1

G1將JAVA的堆空間分割成了若干空間大小的區域,region包括Eden區、Survivor、 Old、 Humongous 四種類型。其中Humongous是一個比較特殊的Old類型,專門放置大類型的對象。這樣的劃分方式意味着不需要一個連續的內存空間管理對象。G1將空間分爲多個區域,優先回收垃圾最多的區域,G1採用的是好的空間中和能力不會乘勝大量的空間碎片。

Region的數值是在1M到32M字節之間的一個2的冪值數,JVM會盡量劃分2048個左右、同等大小的Region。

GI 的一大優勢在於可預測的停頓時間, 能夠儘可能快地在指定時間內完成垃圾回收任務。在 JDKl l 中,已經將 GI 設爲默認 垃圾回收器。
[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-Bmr0UGJ6-1592646953805)(https://cdn.nlark.com/yuque/0/2020/webp/99448/1592533642361-05c937af-c67e-4136-8797-f082a55ac8df.webp)]

ZGC

ZGC和G1類似,但是ZGC的regin的大小更加靈活和動態,zgc的region不會和G1那樣在一開始就被劃分爲固定的regin大小。

zgc的regin核心亮點就是:動態

動態的表現爲:

1、動態地創建和銷燬

2、動態的決定regin的大小,他的最小單位是2MB的一個塊,每一個大小就是2MB*N的大小

切還有一個概念是size groups有三種:

Small:就是一個2MB的regin

Medium:32MB。2MB*16

Large:N*2MB

整個的樣子爲:
[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-FdHh1kr1-1592646953812)(https://cdn.nlark.com/yuque/0/2020/webp/99448/1592533646841-4e658b11-2302-4b01-89cd-404701861fb2.webp)]

JVM內存分配、回收

對象優先在Eden區分配

大多數情況下,對象在新生代中的Eden區分配,當Eden中沒有足夠的空間進行分配的時候,虛擬機講發起一次Minor GC。

Minor GC和Full GC有什麼區別

新生代GC(Minor GC):指發生新生代的垃圾收集動作,Minor GC非常頻繁,回收速度一般比較快。

老年代GC(Major GC/Full GC):指發生在老年代的GC,出現Major GC經常會伴隨至少一次的Minor CG(並非絕對),Major GC的速度一般會比Minor GC慢10背以上。

/**
 * @author yangxinlei
 * @date 2020/6/19
 */
public class GCTest {
    public static void main(String[] args) {
        byte[] allocation1, allocation2;
        allocation1 = new byte[1024*1020*5];
        // allocation2 = new byte[1024*1020*5];
    }
}

在啓動項中增加VM參數爲:-XX:+PrintGCDetails

Heap
 PSYoungGen      total 76288K, used 13352K [0x000000076ae00000, 0x0000000770300000, 0x00000007c0000000) //新生代
  eden space 65536K, 100% used [0x000000076ae00000,0x000000076bb0a108,0x000000076ee00000)
  from space 10752K, 0% used [0x000000076f880000,0x000000076f880000,0x0000000770300000)
  to   space 10752K, 0% used [0x000000076ee00000,0x000000076ee00000,0x000000076f880000)
 ParOldGen       total 175104K, used 0K [0x00000006c0a00000, 0x00000006cb500000, 0x000000076ae00000) // 老年代
  object space 175104K, 0% used [0x00000006c0a00000,0x00000006c0a00000,0x00000006cb500000)
 Metaspace       used 3512K, capacity 4498K, committed 4864K, reserved 1056768K // 原空間
  class space    used 387K, capacity 390K, committed 512K, reserved 1048576K

從上面的數據中可以看到eden中的內存幾乎被分配完全,雖然程序什麼也沒做只是定義出來也用了一部分的空間,我們可以想想到我們再吧allocation2定義出來然後會出現什麼情況。

Heap
 PSYoungGen      total 76288K, used 8252K [0x000000076ae00000, 0x0000000770300000, 0x00000007c0000000)
  eden space 65536K, 12% used [0x000000076ae00000,0x000000076b60f0f8,0x000000076ee00000)
  from space 10752K, 0% used [0x000000076f880000,0x000000076f880000,0x0000000770300000)
  to   space 10752K, 0% used [0x000000076ee00000,0x000000076ee00000,0x000000076f880000)
 ParOldGen       total 175104K, used 0K [0x00000006c0a00000, 0x00000006cb500000, 0x000000076ae00000)
  object space 175104K, 0% used [0x00000006c0a00000,0x00000006c0a00000,0x00000006cb500000)
 Metaspace       used 3512K, capacity 4498K, committed 4864K, reserved 1056768K
  class space    used 387K, capacity 390K, committed 512K, reserved 1048576K

爲什麼會出現這樣的情況 因爲當給2分配內存的時候eden區的內存發現內存已經基本沒有了, 沒有內存的話,虛擬機將會發生一次GC,GC期間的時候虛擬機發現1無法存入Survior空間,所以只好通過 分配擔保機制 吧新生成的對象提前轉移的老年代中,老年代的內存空間足夠方1,所以不會出現Full GC,執行Minor GC後,後面分配的對象如果能夠存eden區的話,還是會在eden區分配內存。

大對象會直接進入老年代

大對象就是需要大量的連續的內存空間對象(比如:字符穿、數組)

爲什麼會這樣的?

爲了避免大對象分配內存時由於分擔機制帶來的複製而降低效率

長期存活的對象將進入老年代

既然虛擬機採用了分代收集的思想來管理內存,那麼內存回收時候就必須能識別哪些對象應方在新生代,哪些應方在老年代中,爲了做到這一點,虛擬機給每個對象一個年紀計數器。

如果對象在 Eden 出生並經過第一次 Minor GC 後仍然能夠存活,並且能被 Survivor 容納的話,將被移動到 Survivor 空間中,並將對象年齡設爲1.對象在 Survivor 中每熬過一次 MinorGC,年齡就增加1歲,當它的年齡增加到一定程度(默認爲15歲),就會被晉升到老年代中。對象晉升到老年代的年齡閾值,可以通過參數 -XX:MaxTenuringThreshold 來設置。

怎麼對象可以被回收

堆中幾乎放着所有的對象實例,對堆垃圾回收前的第一步就是要判斷那些對象已經死亡(即不能再被任何途徑使用的對象)。

引用計數法

對象中添加一個引用計數器,每當一個地方引用他,計數器就會加1,當引用失效,計數器就減1,任何時候計數器爲0的對象就是不可能再被使用的。這個方法實現簡單,效率高,但是目前主流的虛擬中並沒有選擇這個方法來管理內存,主要原因是因爲對象之間會相互調用的問題導致計數器不可能爲0,也就是最後結果導致無法GC回收器回收他們。

public class Reference {
    Object instance = null;
    public static void main(String[] args) {
        Reference a = new Reference();
        Reference b = new Reference();
        a.instance = b;
        b.instance = a;
        a = null;
        b = null;
    }
}

可達性分析算法

這個算法的基本思想就是通過一列被稱爲“GC Roots”的對象作爲起點,從這些點開始向下搜索,節點所走過的路徑被稱爲引用鏈,當一個對象到GC ROOTS沒有任何引用鏈相關聯的話,則證明此對象是不可用的。

GC Roots根節點:類加載器、Thread、虛擬機棧的本地變量表、static成員、常量引用、本地方法棧的變量等等

[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-eewblJtQ-1592646953834)(https://cdn.nlark.com/yuque/0/2020/png/99448/1592646048626-99e79f55-1ccf-4f60-a989-69968e06245b.png)]

finalize()方法最終判定對象是否存活

即使在可達性分析算法中不可達的對象,也並非是“非死不可”的,這時候它們暫時處於“緩刑”階段,要真正宣告一個對象死亡,至少要經歷再次標記過程。

標記的前提是對象在進行可達性分析後發現沒有與GC Roots相連接的引用鏈。

1、 第一次標記並進行一次篩選

篩選的條件是此對象是否有必要執行finalize()方法。

當對象沒有覆蓋finalize方法,或者finzlize方法已經被虛擬機調用過,虛擬機將這兩種情況都視爲“沒有必要執行”,對象被回收。

2. 第二次標記

如果這個對象被判定爲有必要執行finalize()方法,那麼這個對象將會被放置在一個名爲:F-Queue的隊列之中,並在稍後由一條虛擬機自動建立的、低優先級的Finalizer線程去執行。這裏所謂的“執行”是指虛擬機會觸發這個方法,但並不承諾會等待它運行結束。這樣做的原因是,如果一個對象finalize()方法中執行緩慢,或者發生死循環(更極端的情況),將很可能會導致F-Queue隊列中的其他對象永久處於等待狀態,甚至導致整個內存回收系統崩潰。

finalize()方法是對象脫逃死亡命運的最後一次機會,稍後GC將對F-Queue中的對象進行第二次小規模標記,如果對象要在finalize()中成功拯救自己----只要重新與引用鏈上的任何的一個對象建立關聯即可,譬如把自己賦值給某個類變量或對象的成員變量,那在第二次標記時它將移除出“即將回收”的集合。如果對象這時候還沒逃脫,那基本上它就真的被回收了。

如何判斷一個常量是廢棄常量

假如在常量池中存在字符串 “ancdef”,如果當前沒有任何String對象引用該字符串常量的話,就說明常量 “ancdef” 就是廢棄常量,如果這時發生內存回收的話而且有必要的話,“ancdef” 就會被系統清理出常量池。

如何判斷一個類是無用的類

判定一個常量是否是“廢棄常量”比較簡單,而要判定一個類是否是“無用的類”的條件則相對苛刻許多。類需要同時滿足下面3個條件才能算是 “無用的類” :

該類所有的實例都已經被回收,也就是 Java 堆中不存在該類的任何實例。

加載該類的 ClassLoader 已經被回收。

該類對應的 java.lang.Class對象沒有在任何地方被引用,無法在任何地方通過反射訪問該類的方法。

虛擬機可以對滿足上述3個條件的無用類進行回收,這裏說的僅僅是“可以”,而並不是和對象一樣不使用了就會必然被回收。

垃圾收集算法

標記--清楚算法

複製算法

標記--整理算法

分代收集算法

標記–清楚算法

算法分爲"標記"和"清除",先吧所有需要回收的標記出來,然後在標記完成後統一回收對象,他是最基礎的收集算法,銷量還是比較高的但是也會有兩個比較明顯的問題:

效率問題

空間問題: 標記後清理產生大量不連續的碎片
[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-nDHcI4AU-1592646953838)(https://cdn.nlark.com/yuque/0/2020/png/99448/1592646077835-d4b108ed-57e0-454e-b636-8208173d4c6a.png)]

複製算法

爲了解決效率問題,“複製”收集算法出現了。它可以將內存分爲大小相同的兩塊,每次使用其中的一塊。當這一塊的內存使用完後,就將還存活的對象複製到另一塊去,然後再把使用的空間一次清理掉。這樣就使每次的內存回收都是對內存區間的一半進行回收。
[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-HtnnkKEK-1592646953845)(https://cdn.nlark.com/yuque/0/2020/png/99448/1592646083647-a3b2d7c3-906a-4e3f-97d2-c80deba61008.png)]

標記-整理算法

根據老年代的特點特出的一種標記算法,標記過程仍然與“標記-清除”算法一樣,但後續步驟不是直接對可回收對象回收,而是讓所有存活的對象向一段移動,然後直接清理掉端邊界以外的內存。

[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-6o7S5zZv-1592646953848)(https://cdn.nlark.com/yuque/0/2020/png/99448/1592646088694-ee2d0444-577e-4ada-b7c5-39b7fb43cacd.png)]

分代收集算法

這個方式是現在比較常見的方式很多垃圾收集都是採用這種算法。

根據對象存活週期的不同將內存分爲幾塊,一般情況下將java堆分爲新生代和老年代,這樣我們就可以根據各個年代的特點選擇合適的垃圾收集算法。

在新生代中,每次收集都會有大量對象死去,可以選擇複製算法,只需要付出少量對象的複製成本就可以完成每次垃圾收集。

而老年代的對象存活機率是比較高的,而且沒有額外的空間對它進行分配擔保,所以我們必須選擇“標記-清除”或“標記-整理”算法進行垃圾收集。

垃圾收集器

serial收集器  串行

parNew收集器    並行

parallel Scavenge收集器     併發

CMS收集器 併發

G1收集器

如果說收集算法是內存回收的方法論,那麼垃圾收集器就是內存回收的具體實現。

每一種垃圾手機機制都是有各自的優點缺點,我們在使用的時候需要根據具體的業務場景選擇自己適合的垃圾收集器

serial收集器

Serial(串行)收集器收集器是最基本、歷史最悠久的垃圾收集器了。大家看名字就知道這個收集器是一個單線程收集器了。它的 “單線程” 的意義不僅僅意味着它只會使用一條垃圾收集線程去完成垃圾收集工作,更重要的是它在進行垃圾收集工作的時候必須暫停其他所有的工作線程( “Stop The World” ),直到它收集結束。

[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-ZZeeT9CM-1592646953854)(https://cdn.nlark.com/yuque/0/2020/png/99448/1592646100145-2fef8015-0c9b-433e-bcd1-267a8d76d28d.png)]

虛擬機的設計者們當然知道Stop The World帶來的不良用戶體驗,所以在後續的垃圾收集器設計中停頓時間在不斷縮短(仍然還有停頓,尋找最優秀的垃圾收集器的過程仍然在繼續)。

雖然他單線程導致停頓,但是他效率還是比較高相對於其他單線程收集器來說

ParNew收集器

其實是serial收集器多線程版,除了使用多個線程進行垃圾分類外,其餘行爲控制參數,收集算法,回收策略等等,和serial收集器完全一樣

新生代採用複製算法,老年代採用標記-整理算法。

它是許多運行在Server模式下的虛擬機的首要選擇,除了Serial收集器外,只有它能與CMS收集器(真正意義上的併發收集器,後面會介紹到)配合工作。

並行和併發:

並行(Parallel) : 指多條垃圾收集線程並行工作,但此時用戶線程仍然處於等待狀態。適合科學計算、後臺處理等弱交互場景。

併發(Concurrent): 指用戶線程與垃圾收集線程同時執行(但不一定是並行,可能會交替執行),用戶程序在繼續運行,而垃圾收集器運行在另一個CPU上。適合Web應用。

Parallel Scavenge收集器

似於ParNew 收集器,是Server 模式(內存大於2G,2個cpu)下的默認收集器,Parallel Scavenge收集器關注點是吞吐量(高效率的利用CPU)。CMS等垃圾收集器的關注點更多的是用戶線程的停頓時間(提高用戶體驗)。所謂吞吐量就是CPU中用於運行用戶代碼的時間與CPU總消耗時間的比值。

Parallel Scavenge收集器提供了很多參數供用戶找到最合適的停頓時間或最大吞吐量,如果對於收集器運作不太瞭解的話,可以選擇把內存管理優化交給虛擬機去完成也是一個不錯的選擇。

[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-hHdBF7S2-1592646953856)(https://cdn.nlark.com/yuque/0/2020/png/99448/1592646107724-5ebecfc6-f24f-4b7f-ab21-0ecce801ad01.png)]

新生代採用複製算法,老年代採用標記-整理算法。

Serial Old收集器

Serial收集器的老年代版本,它同樣是一個單線程收集器。它主要有兩大用途:一種用途是在JDK1.5以及以前的版本中與Parallel Scavenge收集器搭配使用,另一種用途是作爲CMS收集器的後備方案。

Parallel Old收集器

Parallel Scavenge收集器的老年代版本。使用多線程和“標記-整理”算法。在注重吞吐量以及CPU資源的場合,都可以優先考慮 Parallel Scavenge收集器和Parallel Old收集器。

CMS收集器(-XX:+UseConcMarkSweepGC(主要是old區使用))

CMS(Concurrent Mark Sweep)收集器是一種以獲取最短回收停頓時間爲目標的收集器。它而非常符合在注重用戶體驗的應用上使用,它是HotSpot虛擬機第一款真正意義上的併發收集器,它第一次實現了讓垃圾收集線程與用戶線程(基本上)同時工作。

從名字中的Mark Sweep這兩個詞可以看出,CMS收集器是一種 “標記-清除”算法實現的,它的運作過程相比於前面幾種垃圾收集器來說更加複雜一些。整個過程分爲四個步驟:

初始標記: 暫停所有的其他線程(STW),並記錄下直接與root相連的對象,速度很快 ;

併發標記: 同時開啓GC和用戶線程,用一個閉包結構去記錄可達對象。但在這個階段結束,這個閉包結構並不能保證包含當前所有的可達對象。因爲用戶線程可能會不斷的更新引用域,所以GC線程無法保證可達性分析的實時性。所以這個算法裏會跟蹤記錄這些發生引用更新的地方。

重新標記: 重新標記階段就是爲了修正併發標記期間因爲用戶程序繼續運行而導致標記產生變動的那一部分對象的標記記錄,這個階段的停頓時間一般會比初始標記階段的時間稍長,遠遠比並發標記階段時間短

併發清除: 開啓用戶線程,同時GC線程開始對未標記的區域做清掃。

[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-OdhjxsNc-1592646953859)(https://cdn.nlark.com/yuque/0/2020/png/99448/1592646125304-e17f9bfa-4f96-4f36-8862-c459cf71e679.png)]

從它的名字就可以看出它是一款優秀的垃圾收集器,主要優點:併發收集、低停頓。但是它有下面三個明顯的缺點:

對CPU資源敏感(會和服務搶資源);

無法處理浮動垃圾(在java業務程序線程與垃圾收集線程併發執行過程中又產生的垃圾,這種浮動垃圾只能等到下一次gc再清理了);

它使用的回收算法-“標記-清除”算法會導致收集結束時會有大量空間碎片產生。

CMS的相關參數

-XX:+UseConcMarkSweepGC 啓用cms

-XX:ConcGCThreads:併發的GC線程數(並非STW時間,而是和服務一起執行的線程數)

-XX:+UseCMSCompactAtFullCollection:FullGC之後做壓縮(減少碎片)

-XX:CMSFullGCsBeforeCompaction:多少次FullGC之後壓縮一次(因壓縮非常的消耗時間,所以不能每次FullGC都做)

-XX:CMSInitiatingOccupancyFraction:觸發FulGC條件(默認是92)

-XX:+UseCMSInitiatingOccupancyOnly:是否動態調節

-XX:+CMSScavengeBeforeRemark:FullGC之前先做YGC(一般這個參數是打開的)

-XX:+CMSClassUnloadingEnabled:啓用回收Perm區(jdk1.7及以前)

G1收集器(-XX:+UseG1GC)

G1 (Garbage-First)是一款面向服務器的垃圾收集器,主要針對配備多顆處理器及大容量內存的機器. 以極高概率滿足GC停頓時間要求的同時,還具備高吞吐量性能特徵。

[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-nZLYGXUh-1592646953860)(https://cdn.nlark.com/yuque/0/2020/png/99448/1592646142759-7d97efd4-485f-41e6-8147-9aac860c7544.png)]

[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-PudLPSvE-1592646953863)(https://cdn.nlark.com/yuque/0/2020/png/99448/1592646147086-b7ccdb87-fb20-4315-97b8-70341d669828.png)]

G1將Java堆劃分爲多個大小相等的獨立區域(Region),雖保留新生代和老年代的概念,但不再是物理隔閡了,它們都是(可以不連續)Region的集合。

分配大對象(直接進Humongous區,專門存放短期巨型對象,不用直接進老年代,避免Full GC的大量開銷)不會因爲無法找到連續空間而提前觸發下一次GC。

被視爲JDK1.7中HotSpot虛擬機的一個重要進化特徵。它具備以下特點:

並行與併發: G1能充分利用CPU、多核環境下的硬件優勢,使用多個CPU(CPU或者CPU核心)來縮短Stop-The-World停頓時間。部分其他收集器原本需要停頓Java線程來執行GC動作,G1收集器仍然可以通過併發的方式讓java程序繼續執行。

分代收集: 雖然G1可以不需要其他收集器配合就能獨立管理整個GC堆,但是還是保留了分代的概念。

空間整合: 與CMS的“標記–清理”算法不同,G1從整體來看是基於“標記整理”算法實現的收集器;從局部上來看是基於“複製”算法實現的。

可預測的停頓: 這是G1相對於CMS的另一個大優勢,降低停頓時間是G1 和 CMS 共同的關注點,但G1 除了追求低停頓外,還能建立可預測的停頓時間模型,能讓使用者明確指定在一個長度爲M毫秒的時間片段內完成垃圾收集。

G1收集器的運作大致分爲以下幾個步驟:

**初始標記(initial mark,STW):**在此階段,G1 GC 對根進行標記。該階段與常規的 (STW) 年輕代垃圾回收密切相關。

併發標記(Concurrent Marking): G1 GC 在整個堆中查找可訪問的(存活的)對象。

最終標記(Remark,STW): 該階段是 STW 回收,幫助完成標記週期。

篩選回收(Cleanup,STW): 篩選回收階段首先對各個Region的回收價值和成本進行排序,根據用戶所期望的GC停頓時間來制定回收計劃,這個階段其實也可以做到與用戶程序一起併發執行,但是因爲只回收一部分Region,時間是用戶可控制的,而且停頓用戶線程將大幅提高收集效率。

[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-PAMc0KdR-1592646953867)(https://cdn.nlark.com/yuque/0/2020/png/99448/1592646158767-c139dc3f-c978-436f-a9f5-38b0fee25460.png)]

G1收集器在後臺維護了一個優先列表,每次根據允許的收集時間,優先選擇回收價值最大的Region(這也就是它的名字Garbage-First的由來)。這種使用Region劃分內存空間以及有優先級的區域回收方式,保證了GF收集器在有限時間內可以儘可能高的收集效率。

G1垃圾收集分類

YoungGC

新對象進入Eden區

存活對象拷貝到Survivor區

存活時間達到年齡閾值時,對象晉升到Old區

MixedGC

不是FullGC,回收所有的Young和部分Old(根據期望的GC停頓時間確定old區垃圾收集的優先順序)

global concurrent marking (全局併發標記)

a)	Initial marking phase:標記GC Root,STW
b)	Root region scanning phase:標記存活Region
c)	Concurrent marking phase:標記存活的對象
d)	Remark phase :重新標記,STW
e)	Cleanup phase:部分STW

相關參數

a)	G1MixedGCLiveThresholdPercent Old區的region被回收的時候的存活對象佔比
b)	G1MixedGCCountTarget:一次global concurrent marking之後,最多執行Mixed GC的次數
c)	G1OldCSetRegionThresholdPercent  一次Mixed GC中能被選入CSet的最多old區的region數量

觸發的時機

InitiatingHeapOccupancyPercent:堆佔有率達到這個值則觸發global concurrent marking,默認45%

G1HeapWastePercent:在global concurrent marking結束之後,可以知道區有多少空間要被回收,在每次YGC之後和再次發生Mixed GC之前,會檢查垃圾佔比是否達到了此參數,只有達到了,下次纔會發生Mixed GC

如何選擇垃圾收集器

優先調整堆的大小讓服務器自己來選擇

如果內存小於100M,使用串行收集器

如果是單核,並且沒有停頓時間的要求,串行或JVM自己選擇

如果允許停頓時間超過1秒,選擇並行或者JVM自己選

如果響應時間最重要,並且不能超過1秒,使用併發收集器

下圖有連線的可以搭配使用,官方推薦使用G1,因爲性能高

[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-eQt6tTz6-1592646953872)(https://cdn.nlark.com/yuque/0/2020/png/99448/1592646186952-0682591d-cc79-47aa-9834-7ebe8323726e.png)]

調優

JVM調優主要就是調整下面兩個指標

停頓時間: 垃圾收集器做垃圾回收中斷應用執行的時間。-XX:MaxGCPauseMillis

吞吐量: 花在垃圾收集的時間和花在應用時間的佔比 -XX:GCTimeRatio=,垃圾收集時間佔比:1/(1+n)

GC調優步驟

打印GC日誌

-XX:+PrintGCDetails -XX:+PrintGCTimeStamps -XX:+PrintGCDateStamps -Xloggc:./gc.log

分析日誌得到關鍵性指標
分析GC原因,調優JVM參數

Parallel Scavenge收集器(默認)

分析parallel-gc.log

第一次調優,設置Metaspace大小:增大元空間大小-XX:MetaspaceSize=64M -XX:MaxMetaspaceSize=64M

第二次調優,添加吞吐量和停頓時間參數:-XX:MaxGCPauseMillis=100 -XX:GCTimeRatio=99

第三次調優,修改動態擴容增量:-XX:YoungGenerationSizeIncrement=30

配置CMS收集器

-XX:+UseConcMarkSweepGC 分析cms-gc.log

配置G1收集器

-XX:+UseG1GC

分析g1-gc.log

查看發生MixedGC的閾值:jinfo -flag InitiatingHeapOccupancyPercent 進程id

分析工具:gceasy,GCViewer

G1調優相關

常用參數

a)	-XX:+UseG1GC 開啓G1
b)	-XX:G1HeapRegionSize=n,region的大小,1-32M,2048個
c)	-XX:MaxGCPauseMillis=200 最大停頓時間
d)	-XX:G1NewSizePercent   -XX:G1MaxNewSizePercent
e)	-XX:G1ReservePercent=10 保留防止to space溢出()
f)	-XX:ParallelGCThreads=n SWT線程數(停止應用程序)
g)	-XX:ConcGCThreads=n 併發線程數=1/4*並行

最佳實踐

a) 年輕代大小:避免使用-Xmn、-XX:NewRatio等顯示設置Young區大小,會覆蓋暫停時間目標(常用參數3)

b) 暫停時間目標:暫停時間不要太嚴苛,其吞吐量目標是90%的應用程序時間和10%的垃圾回收時間,太嚴苛會直接影響到吞吐量

是否需要切換到G1

    a)	50%以上的堆被存活對象佔用
    b)	對象分配和晉升的速度變化非常大
    c)	垃圾回收時間特別長,超過1秒

G1調優目標

    a)	6GB以上內存
    b)	停頓時間是500ms以內
    c)	吞吐量是90%以上

GC常用參數

堆棧設置

  -Xss:每個線程的棧大小
  -Xms:初始堆大小,默認物理內存的1/64
  -Xmx:最大堆大小,默認物理內存的1/4
  -Xmn:新生代大小
  -XX:NewSize:設置新生代初始大小
  -XX:NewRatio:默認2表示新生代佔年老代的1/2,佔整個堆內存的1/3。
  -XX:SurvivorRatio:默認8表示一個survivor區佔用1/8的Eden內存,即1/10的新生代內存。
 -XX:MetaspaceSize:設置元空間大小
  -XX:MaxMetaspaceSize:設置元空間最大允許大小,默認不受限制,JVM Metaspace會進行動態擴展。

垃圾回收統計信息

  -XX:+PrintGC
  -XX:+PrintGCDetails
  -XX:+PrintGCTimeStamps 
  -Xloggc:filename

收集器設置

  -XX:+UseSerialGC:設置串行收集器
  -XX:+UseParallelGC:設置並行收集器
  -XX:+UseParallelOldGC:老年代使用並行回收收集器
  -XX:+UseParNewGC:在新生代使用並行收集器
  -XX:+UseParalledlOldGC:設置並行老年代收集器
  -XX:+UseConcMarkSweepGC:設置CMS併發收集器
  -XX:+UseG1GC:設置G1收集器
  -XX:ParallelGCThreads:設置用於垃圾回收的線程數

並行收集器設置

  -XX:ParallelGCThreads:設置並行收集器收集時使用的CPU數。並行收集線程數。
  -XX:MaxGCPauseMillis:設置並行收集最大暫停時間
  -XX:GCTimeRatio:設置垃圾回收時間佔程序運行時間的百分比。公式爲1/(1+n)

CMS收集器設置

  -XX:+UseConcMarkSweepGC:設置CMS併發收集器
  -XX:+CMSIncrementalMode:設置爲增量模式。適用於單CPU情況。
  -XX:ParallelGCThreads:設置併發收集器新生代收集方式爲並行收集時,使用的CPU數。並行收集線程數。
  -XX:CMSFullGCsBeforeCompaction:設定進行多少次CMS垃圾回收後,進行一次內存壓縮
  -XX:+CMSClassUnloadingEnabled:允許對類元數據進行回收
  -XX:UseCMSInitiatingOccupancyOnly:表示只在到達閥值的時候,才進行CMS回收
  -XX:+CMSIncrementalMode:設置爲增量模式。適用於單CPU情況
  -XX:ParallelCMSThreads:設定CMS的線程數量
  -XX:CMSInitiatingOccupancyFraction:設置CMS收集器在老年代空間被使用多少後觸發
  -XX:+UseCMSCompactAtFullCollection:設置CMS收集器在完成垃圾收集後是否要進行一次內存碎片的整理	

G1收集器設置

  -XX:+UseG1GC:使用G1收集器
  -XX:ParallelGCThreads:指定GC工作的線程數量
  -XX:G1HeapRegionSize:指定分區大小(1MB~32MB,且必須是2的冪),默認將整堆劃分爲2048個分區
  -XX:GCTimeRatio:吞吐量大小,0-100的整數(默認9),值爲n則系統將花費不超過1/(1+n)的時間用於垃圾收集
  -XX:MaxGCPauseMillis:目標暫停時間(默認200ms)
  -XX:G1NewSizePercent:新生代內存初始空間(默認整堆5%)
  -XX:G1MaxNewSizePercent:新生代內存最大空間
  -XX:TargetSurvivorRatio:Survivor填充容量(默認50%)
  -XX:MaxTenuringThreshold:最大任期閾值(默認15)
  -XX:InitiatingHeapOccupancyPercen:老年代佔用空間超過整堆比IHOP閾值(默認45%),超過則執行混合收集
  -XX:G1HeapWastePercent:堆廢物百分比(默認5%)
  -XX:G1MixedGCCountTarget:參數混合週期的最大總次數(默認8)
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章