一.在JVM中什么是垃圾?如何判斷一個對象是否可被回收?哪些對象可以作為GC Roots的根
垃圾就是在內存中已經不再被使用到的空間就是垃圾.
1.引用計數法:
內部使用一個計數器,當有對象被引用+1,沒有就-1,但是沒有辦法解決循環引用的問題,JVM不采用此類回收法
2.枚舉根節點可達性分析(GC Root) 它必須是一組活躍的引用
思路:通過一系列名為GC Roots的對象作為起始點,從這個被稱為GC Root的對象開始向下進行搜索,如果一個對象達到GC Roots
沒有任何的引用鏈相連時,這說明此對象不可用,也即給定一個集合的引用作為根出發,通過引用關系遍歷對象圖,能被遍歷到
的對象就判定為存活,沒有遍歷到的就判定為死亡
3.
1.虛擬機棧,棧幀中的局部變量區,也稱為局部變量表中引用的對象
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 需要的微服務名稱
