一.在JVM中什麼是垃圾?如何判斷一個對象是否可被回收?哪些對象能夠做爲GC Roots的根java
垃圾就是在內存中已經再也不被使用到的空間就是垃圾.算法
1.引用計數法:服務器
內部使用一個計數器,當有對象被引用+1,沒有就-1,可是沒有辦法解決循環引用的問題,JVM不採用此類回收法多線程
2.枚舉根節點可達性分析(GC Root) 它必須是一組活躍的引用併發
思路:經過一系列名爲GC Roots的對象做爲起始點,從這個被稱爲GC Root的對象開始向下進行搜索,若是一個對象達到GC Roots微服務
沒有任何的引用鏈相連時,這說明此對象不可用,也即給定一個集合的引用做爲根出發,經過引用關係遍歷對象圖,能被遍歷到高併發
的對象就斷定爲存活,沒有遍歷到的就斷定爲死亡性能
3.大數據
1.虛擬機棧,棧幀中的局部變量區,也稱爲局部變量表中引用的對象spa
2.方法區中的類靜態屬性所引用的對象
3.方法區中常量引用的對象
4.本地方法棧中JNI(native方法)所引用的對象
二.如何查看服務器JVM參數的默認值,如何查看正在運行的Java程序的參數?JVM中的經常使用參數有哪些?能否舉例說明
1.分爲三種參數類型
標配參數(-version,-help,-showversion)
X參數(-Xint 解釋執行,-Xcomp 第一次使用就編譯成本地代碼,-Xmixed 混合模式)
XX參數
布爾值類型 -XX:+/- 某個屬性值 (+表示開啓,-表示關閉)
鍵值對類型 -XX:某個參數 = 某個值
2.分爲兩個步驟
jps -l 查看到正在運行的Java程序的PID
jinfo -flag -具體參數 PID 查看當前正在運行的Java程序的當前參數的信息
jinfo -flags PID 查看當前正在運行的Java程序的全部配置信息
3.
boolean類型的參數
PrintGCDetails 是否打印GC的細節
UseSerialGC 是否使用串行垃圾回收器
key-value類型參數
MetaSpaceSize 設置元空間的大小
MaxTenuringThreshould 設置新生代對象通過多少次能夠晉升到老年代
查看默認的參數
java -XX:+PrintFlagsInitial -verion 查看Java虛擬機在出廠時候的參數配置
其中 = 表示值是多少/是否開啓對應功能 := 表示已經被JVM獲取手動修改過的參數
java -XX:+PrintCommandLineFlags 查看JVM默認的GC算法,jdk1.8 默認server端使用的是串行GC,在1.10以後統一使用G1垃圾收集器
經常使用參數配置
-Xms 設置初始堆內存大小,默認爲主物理內存的1/64 等價於 -XX:InitialHeapSize
-Xmx 設置堆的最大分配內存,默認爲主物理內存的1/4,等價於 -XX:MaxHeapSize
-Xss 設置單個線程的棧的大小,通常默認爲512k-1024k,取決於操做系統,Linux/Unix默認爲1024k,Windows根據虛擬內存大小來決定,默認出廠值爲0
等價於 -XX:ThreadStackSize
-Xmn 設置年輕代的大小,通常不用更改
-XX:MetaSpaceSize 設置元空間的大小,元空間的本質和永久代相似,都是對JVM中方法區的實現,兩者的區別在於,元空間並不在虛擬機中,使用的是本地
的主物理內存,所以在默認狀況下,元空間的大小僅受本地內存空間的限制
-XX:PrintGCDetails 打印出GC收集的詳細日誌信息
-XX:SurvivorRatio 設置Eden區的比例佔多少,S0/S1相同
-XX:NewRatio 設置年輕代和老年代在堆解構的佔比
-XX:NewRatio=2 新生代佔1,老年代佔2,年輕代佔整個堆的1/3
-XX:NewRatio=4 新生代佔1,老年代佔4,年輕代佔整個堆的1/5
-XX:MaxTenuringThreshould 設置新生代對象通過多少次能夠晉升到老年代,默認是15次
典型配置案例
-Xms128m -Xmx4096m -Xss1024k -XX:MetaspaceSize=512m -XX:+PrintCommandLineFlags -XX:+PrintGCDetails -XX:+UseSerialGC
public static void show01(){ System.out.println("****** hello GC ******"); // byte[] byteArr = new byte[50*1024*1024]; try { Thread.sleep(Integer.MAX_VALUE); } catch (InterruptedException e) { e.printStackTrace(); } }
修改前的參數
-XX:InitialHeapSize=265650752
-XX:MaxHeapSize=4250412032
-XX:+PrintCommandLineFlags
-XX:+PrintGCDetails
-XX:+UseCompressedClassPointers
-XX:+UseCompressedOops
-XX:-UseLargePagesIndividualAllocation
-XX:+UseParallelGC
修改後的參數
-XX:InitialHeapSize=134217728
-XX:MaxHeapSize=4294967296
-XX:MetaspaceSize=536870912
-XX:+PrintCommandLineFlags
-XX:+PrintGCDetails
-XX:ThreadStackSize=1024
-XX:+UseCompressedClassPointers
-XX:+UseCompressedOops
-XX:-UseLargePagesIndividualAllocation
-XX:+UseSerialGC
打印的GC的日誌收集信息 規律: [名稱: GC前內存佔用 -> GC後內存佔用(該區內存總大小)]
配置: -Xms10m -Xmx10m -XX:+PrintGCDetails
GC [PSYoungGen: 1366K->496K(2560K)] 1366K->520K(9728K), 0.0005838 secs][Times: user=0.00 sys=0.00, real=0.00 secs] YoungGC前新生代佔用 YoungGC前堆內存佔用 YoungGC耗時 YoungGC用戶耗時 系統耗時 實際耗時 YoungGC後新生代佔用 YoungGC後堆內存佔用 新生代總大小 JVM堆總大小 FULL GC [PSYoungGen: 464K->0K(2560K)] [ParOldGen: 24K->357K(7168K)] 488K->357K(9728K), [Metaspace: 3075K->3075K(1056768K)], 0.0030993 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] Young區 GC前Young區內存佔用 Old區 GC前Old區內存佔用 GC前堆內存佔用 元空間 GC前內存佔用 GC耗時 用戶時間 系統時間 實際時間 GC後Young區內存佔用 GC後Old區內存佔用 GC後堆內存佔用 GC後內存佔用 Young區總大小 Old總大小 JVM堆總大小 元空間總大小
四.經常使用的GC算法有哪些?能否談一談都有垃圾回收器/垃圾回收算法?分別適用於哪些場景?能否詳細的說一下爲何從1.10開始都默認採用G1收集器,談談你的看法?
1.經常使用的GC算法有4種
引用計數/複製/標記整理/標記清除
2.有4種垃圾收集器
1.串行垃圾收集器
它是單線程環境設計且只使用一個進程進行垃圾回收,會暫停全部的用戶線程,因此不適合高併發,快速響應的服務器環境
2.並行垃圾收集器
多個垃圾線程並行工做,此時用戶線程是暫停的,適用於科學計算/大數據處理平臺等弱交互場景使用
3.併發垃圾收集器
用戶線程和垃圾回收線程同時進行,可交互執行,不須要暫停用戶線程,互聯網公司使用的較多,能夠知足對交互時間有需求的場景
4.G1垃圾收集器
將堆內存分割爲不一樣的區域,而後併發的對其進行垃圾回收操做
3.垃圾回收器的類型/垃圾回收算法(垃圾收器就是具體實現這些GC算法並實現內存回收,不一樣版本,不一樣廠商的虛擬機實現的差異很大)
垃圾回收器
1.UseSerialGC
2.UseParNewGC
3.UseParallelGC
4.UseConcMarkSweepGC
5.UseParallelOldGC
6.UseG1GC
垃圾收集算法
1.SerialCopying (Young區)
2.ParallelScavenge(Young區)
3.ParNew(Young區)
4.SerialMSC(Old區)
5.ParallelCompacting(Old區)
6.CMS(Old區)
7.G1(Young&Old)
年輕代(Young區)
串行GC (Serial Copying)
串行GC是最古老,穩定,高效的收集器,只使用一個線程去回收,但其在進行垃圾回收的過程當中可能會產生較長的停頓(SWT)狀態,雖然
在收集垃圾的過程當中須要暫停全部其餘的工做線程,可是它簡單高效,對於限定的單個CPU來講,沒有線程交互的開銷能夠得到最高的單線程
垃圾收集的效率,所以Serial垃圾收集器依然是Java虛擬機在Client模式下默認的新生代收集器
配置: -Xms10m -Xmx10m -XX:+PrintGCDetails -XX:+PrintCommandLineFlags -XX:+UseSerialGC
開啓後會使用Serial + Serial Old的收集組合,表示新生代,老年代都會使用串行垃圾收集器,新生代使用複製算法,老年代使用標記整理算法
並行GC(ParNew)
使用多線程進行垃圾回收,在垃圾收集時會暫停其餘全部的工做線程直到垃圾收集結束.ParNew實際上就是Serial收集器在新生代多線程
版本,最多見的使用場景是配合老年代的CMS工做,其他的和Serial收集器徹底同樣.ParNew在垃圾收集過程當中一樣要暫停其餘全部的工做線程
,它是不少Java虛擬機在Server的默認垃圾收集器
配置:-Xms10m -Xmx10m -XX:+PrintGCDetails -XX:+PrintCommandLineFlags -XX:+UseParNewGC
開啓後會使用ParNew + CMS +Serial Old(備份)的組合,新生代使用複製算法,老年代使用標記整理算法,ParNew+Tenured這樣的組合在Java8之後再也不被推薦
並行GC(Parallel)/(ParallelScavenge)
ParallelScavenge相似於ParNew也是一個新生代垃圾收集器,也是一個基於多線程的垃圾收集器,它是串行收集器正在新生代和老年代的並行化,能夠控制
吞吐量,高吞吐量意味着高效的利用CPU的時間,多用於後臺計算而不須要太多交互的任務.自適應調節策略也是ParallelScavenge收集器和ParNew收集器的一
個很大的區別,JVM會根據當前系統運行的狀況收集性能監控信息,動態調整這些參數以提供最適合的停頓時間或最大吞吐量(-XX:MaxGCPauseMillis)
配置:-Xms10m -Xmx10m -XX:+PrintGCDetails -XX:+PrintCommandLineFlags -XX:+UseParallelGC
開啓後新生代使用複製算法,老年代使用標記整理算法
老年代
CMS收集器(併發標記清除)
是以一種最短回收時間爲目標的收集器,適用於大型的B/S系統的服務器上,重視服務器的響應速度,但願系統的停頓時間最短,適用於堆內存大,CPU核數較多 的服務端應用,也是G1出現以前大型應用首選的垃圾收集器.因爲耗時最長的併發標記和併發清除的過程當中,垃圾收集線程能夠和用戶線程一塊兒工做,因此總體來看
CMS收集器的內存回收和用戶的工做線程是併發執行的.
配置:-Xms10m -Xmx10m -XX:+PrintGCDetails -XX:+PrintCommandLineFlags -XX:+UseConcMarkSweepGC
開啓後將自動打開-XX:+UseParNewGC,使用ParNew(Young區) + CMS(區) + Serial Old的收集器組合,Serial Old做爲CMS出錯的後備收集器
收集步驟:
1.初始標記 只是標記一下GC Root能直接關聯的對象,速度很快,但任然須要暫停全部的工做線程
2.併發標記 進行GC Roots跟蹤過程,和用戶的線程一塊兒,不須要暫停工做線程,主要標記過程,標記所有對象
3.從新標記 爲了修改併發標記的時間,因用戶進程繼續運行而致使標記產生變更的那一部分對象的標記記錄,任然須要暫停全部的工做線程,因爲在
併發標記時,用戶線程依然運行.所以在正式清理前,再作修正
4.併發清除 清除GC Roots不可達對象,和用戶線程一塊兒工做,不須要暫停工做線程,基於標記的結果,直接清理對象
優缺點:
優勢: 併發收集低停頓
缺點: 併發執行,對CPU壓力大(因爲併發進行,CMS在收集與應用線程會同時增長對堆內存的佔用,也就是CMS必需要在老年代堆內存用盡以前完成垃圾
回收,不然CMS回收失敗時,觸發擔保機制,串行老年代收集器將會以SWT的方式進行一次GC,從而形成較大的停頓)
採用標記清除算法會形成大量的碎片(標記清除算法沒法整理空間碎片,老年代空間會隨着應用時長被逐步耗盡最後不得不經過擔保機制對堆內
存進行壓縮,CMS也提供了參數-XX:CMSFullGCsBeForeCompaction(默認爲0,每次都進行內存整理)來指定多少次CMS
收集後,進行一次壓縮的Full GC)
Serial Old收集器
Serial Old是Serial垃圾收集器的老年代版本,是個單線程的收集器,使用標記整理算法,這個收集器也是主要運行在Client的默認的Java虛擬機的老年代垃圾
收集器,如今在JDK1.8以後,已經不推薦使用了.
配置:-Xms10m -Xmx10m -XX:+PrintGCDetails -XX:+PrintCommandLineFlags -XX:+UseSerialOldGC
垃圾收集器組合
1.單CPU或小內存,單機程序 -XX:+UseSerialGC
2.多CPU追求最大吞吐量,如計算後臺的應用, -XX:+UseParallelGC / -XX:+UseParallelOldGC
3.多CPU追求低停頓時間,須要快速響應,如互聯網應用 -XX:+UseConcMarkSweepGC / -XX:+UseParNewGC
參數 | 新生代垃圾收集器 | 新生代算法 | 老年代垃圾收集器 | 老年代算法 |
-XX:UseSerialGC | SerialGC | 複製 | SerialOldGC | 標整 |
-XX:+UseParNewGC | ParNew | 複製 | SerialOldGC | 標整 |
-XX:+UseParallelGC | Parallel[Scavenge] | 複製 | ParallelOldGC | 標整 |
-XX:+UseParallelOldGC | 同上 | 同上 | 同上 | 同上 |
-XX:+UseConcMarkSweepGC | ParNew | 複製 | CMS+Serial Old收集器的組合 Serial Old做爲CMS出錯的後備 |
標清 |
-XX:+UseG1GC | 標清 | 標清 | 標清 | 標清 |
G1垃圾收集器
1.以往垃圾收集器的特色
1.年輕代和老年代必須是各自獨立且連續的內存塊
2.年輕代收集使用單eden+S0+S進行復制算法
3.老年代收集必須掃描整個老年區
4.都已儘量少而快速地執行GC爲設計原則
2.G1收集器的特色
G1是一種面向服務端的垃圾收集器,應用在多處理器和大內存容量的環境中,在實現高吞吐量的同時儘量知足垃圾收集器暫停時間的特性,此外
還具備以下需求:
1.和CMS同樣能夠和應用程序併發執行
2.整理空閒空間速度更快
3.須要更多的時間來預測GC的停頓時間
4.不但願犧牲大量的吞吐性能
5.不須要更大的Java Heap
3.爲何使用G1收集器
1.G1可以充分利用多CPU,多核環境硬件優點,儘可能縮短SWT
2.G1總體上採用標記-整理算法,劇不是經過複製算法,不會產生內存碎片
3.宏觀上看G1之中不在區分年輕代和老年代.把內存劃分紅獨立的子區域Region,可近似理解爲圍棋棋盤
4.G1收集器將整個的內存區域都混在了一塊兒,但其自己依然在小範圍內要進行年輕代和老年代的區分,保留了新生代和老年代,但它們再也不是物理隔離,
而是一部分Region的集合不須要Region是連續的,也就是說依然會採用不一樣的GC方式來處理不一樣的區域
5.G1雖然也是分代收集器,但整個內存區域不存在物理上的年輕代和老年代的區別,也不須要徹底獨立的survivor堆作複製準備.G1只有邏輯上的分代
概念,或者說每一個分區可能隨G1的運行在不一樣代之間先後切換最大的好處是化整爲零,避免了全內存的掃描,只須要按照區域進行掃描便可
4.G1的算法原理
1.G1將堆劃分爲若干個區域,仍然屬於分代收集器,這些Region一部分包含新生代,新生代的垃圾收集依然採用暫停全部線程的方式將存活的對象拷貝到
老年代或者Survivor區.
2.這些Region中一部分包含老年代,G1收集器經過將對象從一個區域複製到另外一個區域完成清理工做,這也意味着,在正常的清理過程當中,G1完成了堆的
壓縮,這樣就再也不會有CMS內存碎片的問題了
3.在G1中還有一種特殊的區域,稱爲Humongous區,若是一個對象的空間超過了分區容量的50%,G1收集器就認爲這是一個巨型的對象,這些巨型對象會
直接的分配在老年代,但若是是一個短時間存在的巨型對象,就會對垃圾收集器形成負面的影響,爲了解決這類問題,G1專門劃分了一塊Humongous區域專門
用來存放巨型對象,若是H區放不下一個巨型對象,G1就會尋找連續的H區來存儲,爲了可以找到聯繫的H區,有時不得不啓用Full GC
5.回收過程
1.Eden區數據轉移到Survivor區,假如Survivor區的內存不夠,Eden區會晉升到Old區
2.Survivor區域的數據會移動到新的Survivor區,部分數據會晉升到Old區
3.最後Eden區回收完畢,GC結束,用戶的進程繼續執行
6.G1經常使用的配置參數
1.-XX:+UseG1GC 使用G1垃圾收集器
2.-XX:G1HeapRegionSize = n 設置G1區域的大小,值爲2的指數冪,範圍是1~32MB,目標是根據Java堆的大小劃分出2048個區域
3.-XX:MaxGCPauseMillis = n 最大GC的停頓時間,這是個軟目標,JVM儘量停頓小於這個時間
4.-XX:InitiatingHeapOccupancyPercent = n 堆佔用多少就觸發GC,通常是45%
5.-XX:ConcGCThread = n 併發GC使用的線程數
6.-XX:G1ReservePercent = n 設置作爲空閒空間的預留內存百分比,以下降目標發生內存溢出的風險,默認值是10%,通常不改
7.和CMS相比有哪些優點
1.G1不會產生內存碎片
2.能夠精確的控制停頓,該收集器把整個堆劃分紅固定大小的區域,每次會根據容許停頓的時間去收集垃圾最多的區域
G1收集器的目標是取代CMS收集器,它同CMS相比,在如下方面更具備優點,主要應用在多CPU和大內存服務器環境下,極大的減小垃圾收集的停頓時間,全面提高
服務器的性能,逐步替代CMS收集器.主要改變的是Eden,Survivor和Tenured等區域再也不是連續的了,而是變成一個個大小同樣的Region,每一個Region從1M到32M
不等.一個Region可能屬於Eden,Survivor,Tenured任意的內存區域.這樣即不會產生內存碎片,同時垃圾收集時間上添加了預測機制,用戶能夠指定但願的停頓時間.
8.配置
1.生產系統配置 -XX:+UseG1GC -Xms32G -XX:MaxGCPauseMillis=100
2.Spring boot微服務 java -server -Xms1024 -Xmx1024m -XX:+UseG1GC -jar 須要的微服務名稱