對於內存當中無用的對象進行回收,如何去判斷一個對象是否是無用的對象。html
每一個對象中都會存儲一個引用計數,每增長一個引用就+1,消失一個引用就-1。當引用計數器爲0時就會判斷該對象是垃圾,進行回收。算法
可是這樣會有一個弊端。就是當有兩個對象互相引用時,那麼這兩個對象的引用計數器都不爲0,那麼就不會對其進行回收。shell
判斷某個對象是否可到達。有兩種方式判斷是否可到達:多線程
直接引用(上帝視角GC Roots):就是虛擬機棧幀中的局部或本地變量表、類加載器、static成員、常量引用、Thread等等中的引用直接到達。併發
爲何本地或局部變量表裏面的變量有它出發就能夠用來判斷GC Roots的判斷標準呢?oracle
由於只用它表示這個棧幀正在被壓棧,正在被使用,這個時候再去回收這個對象不是瘋了嘛!!!同理static、常量也是同樣的道理。app
間接引用:經過別人的引用來達到。maven
併發的可達性分析(併發標記、浮動垃圾):https://mp.weixin.qq.com/s/EgVPlOLArsWb86Kujykn3Aide
標記-清除wordpress
先標記
後清除
弊端一:會有空間碎片問題,空間不連續;這時若是有大一點的對象進來,發現沒有連續的空間內存去進行分配,就會再一次的觸發垃圾回收機制。
弊端二:在標記和清除的過程當中、會掃描整個堆內存;會比較耗時。
有點:簡單、明瞭、好操做。
標記-複製
一開始將這個內存空間一分爲二,兩邊大小相等,一邊使用中的,一邊是保留區未使用的。劃分爲這樣示例圖:
在標記和清除以後,將存活的對象複製到另一邊,在將先前的一邊數據所有清除掉。
以後以此反覆、兩個循環往返。
相似於堆內存中的新生代(Young)區中的Survivor區中的S0、S1,因此堆內存中的新生代(Young)區必定用的就是複製算法。
標記-整理
先標記
後整理。
整理移動以後會獲得一片連續的可分配內存空間。解決了空間碎片的問題,可是這種方式在標記和整理移動的過程當中也是耗時的。
串行:Serial系列;
並行【吞吐量優先】:Paraller系列;
吞吐量:用戶代碼執行的時間 / (用戶代碼執行的時間+垃圾收集時間)99/(99+1)=99%。
適用於後臺運算,不須要太多的交互場景。
併發【停頓時間優先】:CMS、G1;
適用於用戶交互較多的場景,給用戶更好的體驗感;如Web應用。
JVM垃圾收集器調優的原則:儘量在停頓時間較低的狀況下,追求高的吞吐量和少的垃圾回收次數。
官方JVM垃圾收集器建議:
G1(Garbage-First):JDK7出現,JDK8推薦使用,JDK9默認垃圾收集器。
G1的整個垃圾收集並清理的過程階段大致上和CMS收集器是不變的。在最後一個階段進行刪選回收(選擇性的回收,進行優先級的回收:優先回收區域(Region)內存活對象較少的)。
從新設計內存空間如圖所示:
整個內存劃分爲一個個大小相等的區域(Region)。邏輯上對這些區域(Region)進行標記,這些標記有Eden區,Survivor區和Old區。這時的物理空間上就不在是連續空間了;以前的空間劃分都是連續的空間。假如回收掉某個Old區的數據,這時這個區域也可能會標位Survivor區或者Eden區。
區域(Region)內還有一個記錄rememberd Set。之前會全盤掃描堆內存,是比較耗時的。這時會記錄一個對象存活的地方,對象的引用指向;這樣就不用在全盤掃描了耗時比較低。
官方文檔(G1垃圾收集器的前世此生):https://www.oracle.com/technetwork/tutorials/tutorials-1876574.html
Serial:JDK1.3出現的,單線程收集,STW。那時候的CPU仍是單核CPU。單線程處理效率比較高,在進行垃圾回收的時候,會暫停業務線程,等待垃圾回收完成以後,在讓業務線程再繼續執行。會搭配老年代的SerialOld配合使用。
這時會出現Stop The World(STW)
ParNew:並行垃圾收集器多個垃圾線程一塊兒跑,STW ,停頓時間較多,更加關注吞吐量
複製算法、並行多線程垃圾收集器,解決了單線程的侷限性,可是仍是Stop The World(STW)。
ParallelScavenge
同上
CMS:JDK5出現的,併發收集,兩個階段會STW(初始標記、從新標記),更加關注停頓時間。在JDK8中已經不推薦使用,JDK8推薦使用G1收集器。
併發:垃圾收集線程和業務代碼線程一塊兒跑。可是並不能作到全程一塊兒執行。
由於垃圾收集線程在執行的時候對垃圾進行標記,這時業務代碼線程也在執行,也會產生新的垃圾。至少在垃圾收集線程在進行標記的階段,業務代碼暫定的是不執行的。
劃分爲四個階段:初始標記、併發標記、從新標記、併發清理。
初始標記:第一階段會Stop The World(STW)。這個階段執行的時間是很是快的,若是開啓多個線程,會消耗線程以前的切換反而會增長時間成本。
併發標記:第二階段就是可達性分析,對第一階段的垃圾進行跟蹤。在這個階段垃圾線程和業務線程是一塊兒執行的;爲啥能夠一塊兒執行呢?由於在第一階段初始標記完成後大局已定,第二階段的併發標記只是作增量的更新。若是此時又產生了垃圾那麼就是浮動垃圾(把本來消亡的對象錯誤的標記爲存活狀態),只能等待下次清理。
從新標記:第三階段這時會中止業務代碼的線程Stop The World(STW),會多線程垃圾收集器並行一塊兒跑,一塊兒執行。
併發清理:第四階段垃圾收集線程和業務代碼線程再次一塊兒執行,一塊兒跑。
特色:併發收集,停頓時間較少。
缺點:會產生浮動垃圾。其次因爲採用的是標記-清除這樣的算法會產生大量的空間碎片。
Serial Old:串行的
Paraller Old:並行的
如何查看當前JAVA程序應用使用的是什麼垃圾收集器:
# 查看進程ID jps -l 8720 org.jetbrains.jps.cmdline.Launcher 10212 org.jetbrains.idea.maven.server.RemoteMavenServer36 3764 15480 sun.tools.jps.Jps 4216 com.hopefun.scm.WebApplication # 查看當前進程下是否使用UseParallelGC jinfo -flag UseParallelGC 4216 -XX:+UseParallelGC