? ? ? ?線上服務(wù)器因為內(nèi)存溢出導(dǎo)致服務(wù)器假死,部分請求通過nginx轉(zhuǎn)發(fā)到這臺機時訪問報錯,在排查生產(chǎn)問題時順藤摸瓜的就找到了GC回收的問題,下面就來結(jié)合項目問題講解一下GC
1、先通過?jstat -gcutil pid interval(ms)在linux中獲取GC執(zhí)行情況
jstat -gcutil? 7698 1000

這里有11個參數(shù),參數(shù)的意思分別為:
S0:?新生代中Survivor space 0區(qū)已使用空間的百分比
S1: 新生代中Survivor space 1區(qū)已使用空間的百分比
E: 新生代已使用空間的百分比
O: 老年代已使用空間的百分比
P: 永久帶已使用空間的百分比
YGC: 從應(yīng)用程序啟動到當前,發(fā)生Yang GC 的次數(shù)
YGCT: 從應(yīng)用程序啟動到當前,Yang GC所用的時間【單位秒】
FGC: 從應(yīng)用程序啟動到當前,發(fā)生Full GC的次數(shù)
FGCT: 從應(yīng)用程序啟動到當前,F(xiàn)ull GC所用的時間
GCT: 從應(yīng)用程序啟動到當前,用于垃圾回收的總時間【單位秒】
2、下面講講垃圾回收機制GC的工作原理
JVM組成:
1.類加載器(Class?Loader):加載類文件到JVM內(nèi)存。Class loader只管加載,只要符合文件結(jié)構(gòu)就加載,至于能否運行,它不負責(zé),那是由Exectution
Engine 負責(zé)的。
2.運行時數(shù)據(jù)區(qū)(Runtime Data Area)
3.執(zhí)行引擎(Execution
Engine):也叫解釋器,負責(zé)解釋命令,交由操作系統(tǒng)執(zhí)行。
4.本地庫接口(Native
Interface):本地接口的作用是融合不同的語言為java所用

運行時數(shù)據(jù)區(qū):
?(1)堆。堆是java對象的存儲區(qū)域,任何用new字段分配的java對象實例和數(shù)組,都被分配在堆上,java堆可用-Xms和-Xmx進行內(nèi)存控制,jdk1.7以后,運行時常量池從方法區(qū)移到了堆上。
(2)方法區(qū):用于存儲已被虛擬機加載的類信息,常量,靜態(tài)變量,即時編譯器編譯后的代碼等數(shù)據(jù)。
(3)虛擬機棧:虛擬機棧中執(zhí)行每個方法的時候,都會創(chuàng)建一個棧楨用于存儲局部變量表,操作數(shù)棧,動態(tài)鏈接,方法出口等信息。
(4)本地方法棧:與虛擬機發(fā)揮的作用相似,相比于虛擬機棧為Java方法服務(wù),本地方法棧為虛擬機使用的Native方法服務(wù),執(zhí)行每個本地方法的時候,都會創(chuàng)建一個棧幀用于存儲局部變量表,操作數(shù)棧,動態(tài)鏈接,方法出口等信息。
(5)程序計數(shù)器。指示Java虛擬機下一條需要執(zhí)行的字節(jié)碼指令。

判斷垃圾算法:
●引用計數(shù)算法
??? 在 Java 中,引用和對象是有關(guān)聯(lián)的。如果要操作對象則必須用引用進行。因此,很顯然一個簡單
的辦法是通過引用計數(shù)來判斷一個對象是否可以回收。簡單說,即一個對象如果沒有任何與之關(guān) 聯(lián)的引用,即他們的引用計數(shù)都不為 0,則說明對象不太可能再被用到,那么這個對象就是可回收對象。
●可達性分析算法
?? 為了解決引用計數(shù)法的循環(huán)引用問題,Java 使用了可達性分析的方法。通過一系列的“GC roots” 對象作為起點搜索。如果在“GC
roots”和一個對象之間沒有可達路徑,則稱該對象是不可達的。要注意的是,不可達對象不等價于可回收對象,不可達對象變?yōu)榭苫厥諏ο笾辽僖?jīng)過兩次標記
過程。兩次標記后仍然是可回收對象,則將面臨回收。
回收垃圾算法:
●標記清除算法(Mark-Sweep)
●復(fù)制算法(copying)
●標記整理算法(Mark-Compact)
●分代收集算法
標記清除算法(Mark-Sweep)
最基礎(chǔ)的垃圾回收算法,分為兩個階段,標注和清除

????????????????????????????????????????????????????????????缺點:從圖中我們就可以發(fā)現(xiàn),該算法最大的問題是內(nèi)存碎片化嚴重,后續(xù)可能發(fā)生大對象不能找到可利用空間的問題。
復(fù)制算法(copying)
為了解決 Mark-Sweep 算法內(nèi)存碎片化的缺陷而被提出的算法。按內(nèi)存容量將內(nèi)存劃分為等大小
的兩塊。每次只使用其中一塊,當這一塊內(nèi)存滿后將尚存活的對象復(fù)制到另一塊上去,把已使用 的內(nèi)存清掉,如圖:

缺點:這種算法雖然實現(xiàn)簡單,內(nèi)存效率高,不易產(chǎn)生碎片,但是最大的問題是可用內(nèi)存被壓縮到了原本的一半。且存活對象增多的話,Copying 算法的效率會大大降低。
標記整理算法(Mark-Compact)
結(jié)合了以上兩個算法,為了避免缺陷而提出。標記清除階段和 Mark-Sweep 算法相同,最后將存活的對象整理到內(nèi)存的一端。如圖:

分代收集算法
1. 新生代
是用來存放新生的對象。一般占據(jù)堆的 1/3 空間。由于頻繁創(chuàng)建對象,所以新生代會頻繁觸發(fā)MinorGC 進行垃圾回收。新生代又分為 Eden 區(qū)、ServivorFrom、ServivorTo 三個區(qū)。
1.1. Eden 區(qū)
Java 新對象的出生地(如果新創(chuàng)建的對象占用內(nèi)存很大,則直接分配到老年代)。當 Eden 區(qū)內(nèi)存不夠的時候就會觸發(fā) MinorGC,對新生代區(qū)進行一次垃圾回收。
1.2. ServivorFrom上一次 GC 的幸存者,作為這一次 GC 的被掃描者。
1.3. ServivorTo保留了一次 MinorGC 過程中的幸存者。
1.4. MinorGC 的過程(復(fù)制->清空->互換)MinorGC 采用復(fù)制算法。
2.老年代.
在老年代-標記整理算法 因為對象存活率高、沒有額外空間對它進行分配
擔(dān)保, 就必須采用“標記—清除”或“標 記—整理”算法來進行回收,
不必進行內(nèi)存復(fù)制, 且直接騰出空閑內(nèi)存.
垃圾回收器
●Serial 垃圾收集器(單線程、復(fù)制算法)
●ParNew 垃圾收集器(Serial+多線程)
●Parallel
Scavenge 收集器(多線程復(fù)制算法、高效)
●Serial Old 收集器(單線程標記整理算法
)
●Parallel Old 收集器(多線程標記整理算法)
●CMS 收集器(多線程標記清除算法)
●G1
●ZGC?

●1.兩個收集器之間有連線,表明它們可以搭配使用。
●2.其中Serial Old作為CMS出現(xiàn)"Concurrent Mode Failure"失敗的后備預(yù)案。
●3.(紅色虛線)由于維護和兼容性測試的成本,再JDK8時將Serial+CMS、ParNew+Serial Old這兩個組合聲明為廢棄,并在JDK9完全取消了這些組合的支持,即移除。
●4.(綠色虛線)JDK14中,棄用Parallel Scavenge+SerialOld組合。刪除CMS垃圾回收器。
7種垃圾回收器的區(qū)別與特點
