1. 如何計算對象已死
1.1 引用計數器算法
引用計數器算法是給每個對象設置一個計數器,當有地方引用這個對象的時候,計數器+1,當引用失效的時候,計數器-1,當計數器為0的時候,JVM就認為對象不再被使用,是「垃圾」了。
引用計數器實現簡單,效率高;但是不能解決循環引用問問題(A對象引用B對象,B對象又引用A對象,但是A,B對象已不被任何其他對象引用),同時每次計數器的增加和減少都帶來了很多額外的開銷,所以在JDK1.1之後,這個算法已經不再使用了。
1.2 可達性分析算法
可達性分析算法是通過一些「GC Roots」對象作為起點,從這些節點開始往下搜索,搜索通過的路徑成為引用鏈(Reference Chain),當一個對象沒有被GC Roots的引用鏈連接的時候,說明這個對象是不可用的,如下圖所示。
如上圖種的對象C,此對象沒有被GC Roots節點引用,就是可回收垃圾。
object5/6/7雖然內部引用,但是沒有被GC Roots,也是垃圾。
那麼,什麼的東西才可以作為GC Roots?
GC Roots對象包括:
- 虛擬機棧(棧幀中的本地變量表)中的引用的對象。
- 方法區域中的類靜態屬性引用的對象。
- 方法區域中常量引用的對象。
- 本地方法棧中JNI(Native方法)的引用的對象。
2.常見的垃圾回收算法
3種:複製;標記清除(Mark-Sweep);標記整理(Mark-Compact);
1.複製算法(年輕代)
複製算法是把內存分成大小相等的兩塊,每次使用其中一塊,當垃圾回收的時候,把存活的對象複製到另一塊上,然後把這塊內存整個清理掉。這種方式聽上去確實是非常不錯的方案,但是總的來說對內存的消耗十分高。
複製之後有交換,誰空誰是To。 詳細如下(複製->清空->互換):
a.Eden和From復,制到To,年齡+1
首先,當Eden區滿時,會觸發第一次gc,把還活著的對象,複製到from區;當Eden區再次發生gc時,會掃描Eden和From這兩個區域,對這兩個區域進行垃圾回收,把還活著的對象,直接複製到To區(如果有對象年齡達到老年標準,則複製到老年代),同時把對象的年齡+1。
b.清空Eden和From
然後,清空Eden和From種的對象;
c.To和From互換
最後,To和From互換,此時From為空,變為To區;原來的To,變為下一次gc時的From區;部分對象會在From和To之間來回複製,交換15次(由jvm參數MaxTenuringThreshold決定,這個參數默認值是15)之後,如果對象還存活,就存入到老年代。
2.標記清除(Mark-Sweep)(老年代)
標記—清除算法包括兩個階段:「標記」和「清除」。在標記階段,確定所有要回收的對象,並做標記。清除階段緊隨標記階段,將標記階段確定不可用的對象清除。
優點:沒有複製,節省空間;
缺點:產生內存碎片;
3.標記壓縮整理(Mark-Compact,或稱為標記-整理算法,java堆中老年代的垃圾回收算法)
優點:在標記清除的基礎上,增加滑動,解決了碎片問題;
缺點:滑動(移動)對象需要成本。
4.分代收集算法
分代收集是根據對象的存活時間把內存分為新生代和老年代,根據個代對象的存活特點,每個代採用不同的垃圾回收算法。新生代採用標記—複製算法,老年代採用標記清除或者標記—整理算法。
垃圾算法的實現涉及大量的程序細節,而且不同的虛擬機平台實現的方法也各不相同。上面介紹的只不過是基本思想。
垃圾收集器有哪些?
上面是目前比較常用的垃圾收集器,和他們直接搭配使用的情況,上面是新生代收集器,下面則是老年代收集器,這些收集齊都有自己的特點,根據不同的業務場景進行搭配使用。
年輕代收集器如下
Serial收集器 (串行)
它針對單線程環境設計,且只使用一個線程進行垃圾回收,會暫停所有用戶線程,所以不適合伺服器環境。
形象解釋:大家在餐廳用餐(用戶線程),來了一位員工阿姨(垃圾回收線程),說我們要打掃衛生了,請大家先暫停用餐,打掃完再繼續吃飯。
ParNew收集器
ParNew收集器其實就是serial收集器的多線程版本,除了使用多條線程進行垃圾收集之外,其餘行為與Serial收集器一樣。 使用方式可以使用-XX:+UseConcMarkSweepGC,或者是使用-XX:+UseParNewGC來強制開啟,可以通過-XX:ParallelGCThreads 來調整或者限制垃圾收集的線程數量。
Parallel Scavenge收集器(並行)
有多個垃圾收集線程並行工作,此時用戶線程也是暫停的;適用於科學計算/大數據後台處理等,和前台若交互場景;
形象解釋:大家在餐廳用餐(用戶線程),來了多個員工阿姨(垃圾回收線程),說我們要打掃衛生了,請大家先暫停用餐,打掃完再繼續吃飯。
Parallel Scavenge收集器也是一個並行的多線程新生代收集器,它也使用複製算法。Parallel Scavenge收集器的特點是它的關注點與其他收集器不同,CMS等收集器的關注點是儘可能縮短垃圾收集時用戶線程的停頓時間,而Parallel Scavenge收集器的目標是達到一個可控制的吞吐量(Throughput)。
特點:
就是非常關注系統的吞吐量,吞吐量=代碼運行時間/(代碼運行時間+垃圾收集時間)
老年代垃圾回收器
Serial Old收集器
Serial Old 是 Serial收集器的老年代版本,它同樣是一個單線程收集器,使用「標記-整理」(Mark-Compact)算法。
用途
- 一個是在JDK1.5及之前的版本中與Parallel Scavenge收集器搭配使用,
- 另一個就是作為CMS收集器的後備預案,如果CMS出現Concurrent Mode Failure,則SerialOld將作為後備收集器。
Parallel Old收集器
Parallel Old收集器是Parallel Scavenge收集器的老年代版本,使用多線程和「標記-整理」算法。前面已經提到過,這個收集器是在JDK 1.6中才開始提供的,在此之前,如果新生代選擇了Parallel Scavenge收集器。
老年代除了Serial Old以外別無選擇,所以在Parallel Old誕生以後,「吞吐量優先」收集器終於有了比較名副其實的應用組合,在注重吞吐量以及CPU資源敏感的場合,都可以優先考慮Parallel Scavenge加Parallel Old收集器。
CMS收集器
CMS(Concurrent Mark Sweep)收集器是一種以獲取最短回收停頓時間為目標的收集器,它非常符合那些集中在網際網路站或者B/S系統的服務端上的Java應用,這些應用都非常重視服務的響應速度。從名字上(「Mark Sweep」)就可以看出它是基於「標記-清除」算法實現的。
CMS收集器工作的整個流程分為以下4個步驟:
- 初始標記(CMS initial mark):僅僅只是標記一下GC Roots能直接關聯到的對象,速度很快,需要「Stop The World」。
- 並發標記(CMS concurrent mark):進行GC Roots Tracing的過程,在整個過程中耗時最長。
- 重新標記(CMS remark):為了修正並發標記期間因用戶程序繼續運作而導致標記產生變動的那一部分對象的標記記錄,這個階段的停頓時間一般會比初始標記階段稍長一些,但遠比並發標記的時間短。此階段也需要「Stop The World」。
- 並發清除(CMS concurrent sweep)
優點
CMS是一款優秀的收集器,它的主要優點在名字上已經體現出來了:並發收集、低停頓,因此CMS收集器也被稱為並發低停頓收集器(Concurrent Low Pause Collector)。
缺點
- 對CPU資源非常敏感 其實,面向並發設計的程序都對CPU資源比較敏感。在並發階段,它雖然不會導致用戶線程停頓,但會因為占用了一部分線程(或者說CPU資源)而導致應用程式變慢,總吞吐量會降低。
- CMS默認啟動的回收線程數是(CPU數量+3)/4,也就是當CPU在4個以上時,並發回收時垃圾收集線程不少於25%的CPU資源,並且隨著CPU數量的增加而下降。但是當CPU不足4個時(比如2個),CMS對用戶程序的影響就可能變得很大,如果本來CPU負載就比較大,還要分出一半的運算能力去執行收集器線程,就可能導致用戶程序的執行速度忽然降低了50%,其實也讓人無法接受。
- 無法處理浮動垃圾(Floating Garbage) 可能出現「Concurrent Mode Failure」失敗而導致另一次Full GC的產生。
- 由於CMS並發清理階段用戶線程還在運行著,伴隨程序運行自然就還會有新的垃圾不斷產生。這一部分垃圾出現在標記過程之後,CMS無法再當次收集中處理掉它們,只好留待下一次GC時再清理掉。
- 這一部分垃圾就被稱為「浮動垃圾」。也是由於在垃圾收集階段用戶線程還需要運行,那也就還需要預留有足夠的內存空間給用戶線程使用,因此CMS收集器不能像其他收集器那樣等到老年代幾乎完全被填滿了再進行收集,需要預留一部分空間提供並發收集時的程序運作使用。
- 標記-清除算法導致的空間碎片 CMS是一款基於「標記-清除」算法實現的收集器,這意味著收集結束時會有大量空間碎片產生。
- 空間碎片過多時,將會給大對象分配帶來很大麻煩,往往出現老年代空間剩餘,但無法找到足夠大連續空間來分配當前對象。
G1收集器
①. G1(Garbage-First)是一款面向服務端應用的垃圾收集器,主要針對配備多核CPU及大容量內存的機器,以極高概率滿足GC停頓時間的同時,還兼具高吞吐量的性能特徵
具體細節詳見 https://juejin.cn/post/7010034105165299725
JDK 8 默認使用的垃圾收集器
查看步驟:
cmd執行命令:
java -XX:+PrintCommandLineFlags -version
輸出如下:
引用類型和垃圾回收
參考文章:
https://blog.csdn.net/csdn_20150804/article/details/96368802
https://wangkang007.gitbooks.io/jvm/content/chapter1.html
https://www.jianshu.com/p/a9703d82c901
https://blog.csdn.net/qq9808/article/details/80933396
https://cloud.tencent.com/developer/article/1592943